include m4/include.am
-if HAVE_LIBEVENT
-include memcached/include.am
-libtest_libtest_la_DEPENDENCIES+= memcached/memcached
-endif
-
docs: html man
TESTS += ${check_PROGRAMS}
namespace libtest {
-Framework::Framework(libtest::SignalThread& signal,
+Framework::Framework(libtest::SignalThread& signal_,
const std::string& name_,
const std::string& only_run_arg,
const std::string& wildcard_arg) :
_runner(NULL),
_socket(false),
_creators_ptr(NULL),
- _signal(signal),
+ _signal(signal_),
_only_run(only_run_arg),
_wildcard(wildcard_arg),
_name(name_)
_options.push_back(std::make_pair(arg, std::string()));
}
-void Server::add_option(const std::string& name, const std::string& value)
+void Server::add_option(const std::string& name_, const std::string& value)
{
- _options.push_back(std::make_pair(name, value));
+ _options.push_back(std::make_pair(name_, value));
}
bool Server::set_socket_file()
AX_APPEND_COMPILE_FLAGS([-Wctor-dtor-privacy])
AX_APPEND_COMPILE_FLAGS([-Wfloat-equal])
AX_APPEND_COMPILE_FLAGS([-Wformat=2])
-AX_APPEND_COMPILE_FLAGS([-Wlong-long])
+dnl AX_APPEND_COMPILE_FLAGS([-Wlong-long])
AX_APPEND_COMPILE_FLAGS([-Wmaybe-uninitialized])
AX_APPEND_COMPILE_FLAGS([-Wmissing-field-initializers])
AX_APPEND_COMPILE_FLAGS([-Wmissing-noreturn])
+++ /dev/null
-steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist, AddToSVNDir
-
-AddToSVNDir.dir = ../website/dist
-svn.tagpattern = %v
+++ /dev/null
-Anatoly Vorobey <mellon@pobox.com>
-Brad Fitzpatrick <brad@danga.com>
+++ /dev/null
-Ideally, you want to make a static binary, otherwise the dynamic
-linker pollutes your address space with shared libs right in the
-middle. (NOTE: actually, this shouldn't matter so much anymore, now
-that we only allocate huge, fixed-size slabs)
-
-Make sure your libevent has epoll (Linux) or kqueue (BSD) support.
-Using poll or select only is slow, and works for testing, but
-shouldn't be used for high-traffic memcache installations.
-
-To build libevent with epoll on Linux, you need two things. First,
-you need /usr/include/sys/epoll.h . To get it, you can install the
-userspace epoll library, epoll-lib. The link to the latest version
-is buried inside
-http://www.xmailserver.org/linux-patches/nio-improve.html ; currently
-it's http://www.xmailserver.org/linux-patches/epoll-lib-0.9.tar.gz .
-If you're having any trouble building/installing it, you can just copy
-epoll.h from that tarball to /usr/include/sys as that's the only thing
-from there that libevent really needs.
-
-Secondly, you need to declare syscall numbers of epoll syscalls, so
-libevent can use them. Put these declarations somewhere
-inside <sys/epoll.h>:
-
-#define __NR_epoll_create 254
-#define __NR_epoll_ctl 255
-#define __NR_epoll_wait 256
-
-After this you should be able to build libevent with epoll support.
-Once you build/install libevent, you don't need <sys/epoll.h> to
-compile memcache or link it against libevent. Don't forget that for epoll
-support to actually work at runtime you need to use a kernel with epoll
-support patch applied, as explained in the README file.
-
-BSD users are luckier, and will get kqueue support by default.
-
-
-
+++ /dev/null
-Want to contribute? Up-to-date pointers should be at:
-
- http://contributing.appspot.com/memcached
+++ /dev/null
-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.
+++ /dev/null
-2010-10-11
- * ChangeLog is no longer being updated.
- See http://code.google.com/p/memcached/wiki/ReleaseNotes
-
-2009-04-10
-
- * *critical bugfix*. In 1.2.7 under multithreaded mode, memcached
- would never restart accepting connections after hitting the
- maximum connection limit.
-
-2009-04-02
-
- * Fix: Rewrote stat buffer handling (Trond)
-
-2009-03-31
-
- * Feature: -b option for configuring backlog
-
- * Feature: stats settings
-
- * Fix: binary stat alignment bug (bug26)
-
- * Fix: buffer overflow in stats (bug27)
-
- * Fix: recycle memory more aggressively (bug14)
-
- * Fix: incr validation (bug31)
-
- * Fix: 64-bit incr/decr delta problem (bug21)
-
- * Fix: Ascii UDP set (bug36)
-
- * Fix: stats slabs' used chunks (bug29)
-
- * Fix: stats reset resetting more (bug22)
-
- * Misc: More tests, documentation, cleanliness (godliness)
-
- * Stable merge (stats, debuggability, listen bugs)
-
-2009-03-11
-
- * Protocol: Binary complete (Dustin, Trond, Toru, etc...)
-
- * Performance: improvements from facebook (Dormando)
-
- * Performance: per-thread stats (Trond)
-
- * Performance: Hash expansion in its own thread (Trond)
-
- * Stats: Lots of new stats (Dustin)
-
- * Bug fixes (various)
-
- See the 1.3.2 release notes or revision control for more detail.
-
-2008-09-06
- * Display error status on listen failures (Dormando)
-
- * Remove managed instance code. Incomplete/etc. (Dormando)
-
- * Handle broken IPV6 stacks better (Brian Aker)
-
- * Generate warnings on setsockopt() failures (Brian Aker)
-
- * Fix some indentation issues (Brian Aker)
-
- * UDP/TCP can be disabled by setting their port to zero (Brian Aker)
-
- * Zero out libevent thread structures before use (Ricky Zhou)
-
- * New stat: Last accessed time for last evicted item per slab class.
- (Dormando)
-
- * Use a dedicated socket accept thread (Facebook)
-
- * Add -R option. Limit the number of requests processed by a connection
- at once. Prevents starving other threads if bulk loading. (Facebook)
-
-2008-07-29 [Version 1.2.6 released]
-
-2008-07-24 [Version 1.2.6-rc1 released]
-
- * Add support for newer automake (Facebook)
-
- * DTrace support for Solaris/etc (Trond Norbye)
-
- * LRU tests (Steve Yen)
-
- * Handle negative length items properly (Dormando)
-
- * Don't leave stale data after failed set attempts (Dormando)
-
- * Fix refcount leaks, which would result in OOM's on all sets
- (Dormando)
-
- * Fix buffer overruns (Dustin Sallings, Tomash Brechko)
-
- * Fix memory corruption with CAS (Dustin Sallings)
-
-2008-06-11
-
- * Fix -k to work with -d. (reported by Gary Zhu)
-
-2008-03-02 [Version 1.2.5-rc1 released]
-
- * Add per-item-class tracking of evictions and OOM errors (dormando)
-
- * Optimize item_alloc() a little (dormando)
-
- * Give 'SERVER_ERROR out of memory' errors more context (dormando)
-
- * Enable usage of large memory pages under solaris
- (Trond.Norbye@Sun.COM)
-
- * Enable UDP by default, clean up server socket code
- (brian@tangent.org)
-
- * 'noreply' support (Tomash Brechko)
-
- * IPv6 support, and IPv6 multi-interface support (brian@tangent.org)
-
- * Add compiler options for Sun Studio compilers with --enable-threads
- (Trond.Norbye@Sun.COM)
-
- * Add --enable-64bit for mulitarget platforms (Trond.Norbye@Sun.COM)
-
- * Use gettimeofday(2) instead of time(2).
-
- * Make -k option work (Tomash Brechko)
-
- * Fix chunk slab alignment (Trond.Norbye@Sun.COM)
-
-2007-12-06 [Version 1.2.4 released]
-
-2007-12-05
-
- * Fix compilation on panther (JS and Dormando)
-
- * More CAS tests (Chris Goffinet)
-
- * Final fixes for all 1.2.4 features are in, -rc2 sent out.
-
-2007-11-19 [Version 1.2.4-rc1 released]
-
-2007-11-19 Dormando <dormando@rydia.net>
-
- * Patch series from Tomash Brechko <tomash.brechko@gmail.com>:
- Minor fixes and optimisations.
-
- * Patches from Chris, Dustin, and Dormando to fix CAS.
-
- * Prepping for 1.2.4 release.
-
-2007-11-13 Dormando <dormando@rydia.net>
-
- * Adjusted patch from js <ebgssth@gmail.com>: Compile on OS X Panther
- and earlier.
-
-2007-11-12 Steven Grimm <sgrimm@facebook.com>
-
- * Patch from Tomash Brechko <tomash.brechko@gmail.com>: Always send
- "SERVER_ERROR out of memory" when memory exhausted.
-
-2007-10-15 Paul Lindner <lindner@inuus.com>
-
- * Patch from David Bremner <bremner@unb.ca> that implements
- a new option "-a" which takes an octal permission mask
- (like chmod) sets the permissions on the unix domain socket
- (specified by "-s").
-
-2007-10-03 Paul Lindner <lindner@inuus.com>
- * Incorporate "cas" operation developed by Dustin
- Sallings <dustin@spy.net> This change allows you
- to do atomic changes to an existing key.
-
- * Fix for stats.evictions not incrementing
- when exptime == 0 items are kicked off the cache.
- from Jean-Francois BUSTARRET <jfbustarret@wat.tv>.
-
- * Fix for do_item_cachedump() which was returning
- an incorrect timestamp.
-
- * Switch to unsigned 64-bit increment/decrement counters
- from Evan Miller and Dustin Sallings.
-
- * Add append command support written by Filipe Laborde.
- Thread safe version plus prepend command from Maxim Dounin
- <mdounin@mdounin.ru>
-
- * The memcached-tool script can now display stats. Patch
- provided by Dan Christian <dchristian@google.com>
-
- * Fix for Unix Domain sockets on FreeBSD
- FreeBSD's sendmsg() requires msg_name in msghdr structure
- to be NULL if not used, setting msg_namelen to 0 isn't enough.
- Patch from Maxim Dounin <mdounin@mdounin.ru>
-
-2007-08-21 Paul Lindner <lindner@inuus.com>
- * Incorporate incrememnt patch from Evan Miller
- <emiller@imvu.com> to define increment overflow
- behavior.
-
-2007-08-07 Leon Brocard <acme@astray.com>
- * Bring the memcached.1 manpage up to date
-
-2007-08-06 Paul Lindner <lindner@inuus.com>
- * Fix crash when using -P and -d flags on x86_64
- with latest libevent release.
-
-2007-07-08 Steven Grimm <sgrimm@facebook.com>
-
- * Item stats commands weren't thread-safe; wrap them with locks
- when compiled in multithreaded mode.
- * The "stats items" command now works again; it broke with the
- introduction of the powers-of-N chunk size change.
-
-2007-07-06 [Version 1.2.3 released]
-
-2007-06-19 Paul Lindner <lindner@mirth.inuus.com>
-
- * Solaris portability fixes from Trond Norbye
-
-2007-05-29 Paul Lindner <lindner@mirth.inuus.com>
-
- * Properly document evictions statistic value
-
-2007-05-10 Paul Lindner <lindner@inuus.com>
-
- * Flesh out tests for unix domain sockets and binary data.
- * Update rpm spec file to run tests
-
-2007-05-07 Paul Lindner <lindner@inuus.com>
-
- * Fix compilation bug on freebsd 6.x (and maybe others)
- * Update RPM spec file per redhat bugzilla #238994
- * Move unistd.h to memcached.h to get rid of warnings
- * Add string.h to thread.c to get correctly prototyped strerror()
-
-2007-05-04 Paul Lindner <lindner@inuus.com>
-
- * Add fedora/redhat style init script and RPM spec file
-
-2007-05-12 [Version 1.2.2 released]
-
-2007-04-16 Steven Grimm <sgrimm@facebook.com>
-
- * Command tokenizer performance and cleanliness improvement.
- Patch contributed by Paolo Borelli <paolo.borelli@gmail.com>.
-
-2007-04-16 Paul Lindner <lindner@inuus.com>
-
- * Add notes to README about MacOS, libevent and kqueue.
-
- * Windows Patch integration -- part 1, warnings elimination.
-
-2007-04-12 Paul Lindner <lindner@mirth.inuus.com>
-
- * Allow changes to the verbosity level of the server with a new
- "verbosity" command and some compiler cleanups.
- Patch contributed by Paolo Borelli <paolo.borelli@gmail.com>.
-
-2007-04-08 Paul Lindner <lindner@inuus.com>
-
- * Add cleanup patch from "Tim Yardley" <liquid@haveheart.com> to
- clean up source spacing issues, fix -Wall warnings, add some
- null checks, adds asserts at the top of each function for any
- use of conn *c without checking to see if c is NULL first.
-
- * Also adjust clean-whitespace.pl to clean *.ac files. Add
- script to test-suite to test for tabs.
-
-2007-04-04 Paul Lindner <lindner@inuus.com>
-
- * Add clarification of flush_all in the protocol docs
- from Elizabeth Mattijsen <liz@dijkmat.nl>
-
-2007-03-31 Paul Lindner <lindner@inuus.com>
-
- * Add patch from Eli Bingham <eli@pandora.com> to
- re-enable the -n switch to memcached.
-
-2007-03-20 Paul Lindner <lindner@inuus.com>
- * Add patch to collect eviction statistics from
- Jean-Francois BUSTARRET <jfbustarret@wat.tv>.
-
- * Updated docs, added new test cases for t/stats.t
-
-2007-03-18 Paul Lindner <lindner@inuus.com>
-
- * Add more test cases using larger buffer sizes up to and greater
- than 1MB.
-
- * Remove unused parameter to item_size_ok()
-
- * Use a single printf() in usage()
-
- * Add a failing test for conforming with maximum connections.
-
-2007-03-17
- * crash fix from Thomas van Gulick <thomas@partyflock.nl> in
- conn_shrink(), passing &ptr, instead of ptr to realloc().
-
-2007-03-05 Paul Lindner <lindner@inuus.com>
- * Fix a number of places where (s)printf calls were using unsigned
- or signed formats that did not match their arguments.
-
- * Add support for stdbool.h and stdint.h to use the bool and
- uint8_t types.
-
- * Major refactoring - move API calls for assoc/items/slabs to
- their own individual header files. Add apropriate const and
- static declarations as appropriate.
-
- * Avoid type-punning. Do a more efficient realloc inside the
- conn_shrink routine.
-
- * Fix overflow bug where uninitialized access to slabclass caused
- size-0 mallocs during slab preallocation.
-
- * Use EXIT_SUCCESS/EXIT_FAILURE constants.
-
- * Convert some sprintf calls to snprintf to protect against
- buffer overflows.
-
- * Explicitly compare against NULL or zero in many places.
-
-2007-03-05
- * Steven Grimm <sgrimm@facebook.com>: Per-object-type stats collection
- support. Specify the object type delimiter with the -D command line
- option. Turn stats gathering on and off with "stats detail on" and
- "stats detail off". Dump the per-object-type details with
- "stats detail dump".
-
-2007-03-01
- * Steven Grimm <sgrimm@facebook.com>: Fix an off-by-one error in the
- multithreaded version's message passing code.
-
-2006-12-23
- * fix expirations of items set with absolute expiration times in
- the past, before the server's start time. bug was introduced in
- 1.2.0 with rel_time_t. Thanks to Adam Dixon
- <adamtdixon@gmail.com> for the bug report and test case!
-
-2006-11-26
- * Steven Grimm <sgrimm@facebook.com>: Performance improvements:
-
- Dynamic sizing of hashtable to reduce collisions on very large
- caches and conserve memory on small caches.
-
- Only reposition items in the LRU queue once a minute, to reduce
- overhead of accessing extremely frequently-used items.
-
- Stop listening for new connections until an existing one closes
- if we run out of available file descriptors.
-
- Command parser refactoring: Add a single-pass tokenizer to cut
- down on string scanning. Split the command processing into
- separate functions for easier profiling and better readability.
- Pass key lengths along with the keys in all API functions that
- need keys, to avoid needing to call strlen() repeatedly.
-
-2006-11-25
- * Steve Peters <steve@fisharerojo.org>: OpenBSD has a malloc.h,
- but warns to use stdlib.h instead
-
-2006-11-22
- * Steven Grimm <sgrimm@facebook.com>: Add support for multithreaded
- execution. Run configure with "--enable-threads" to enable. See
- doc/threads.txt for details.
-
-2006-11-13
- * Iain Wade <iwade@optusnet.com.au>: Fix for UDP responses on non-"get"
- commands.
-
-2006-10-15
- * Steven Grimm <sgrimm@facebook.com>: Dynamic sizing of hashtable to
- reduce collisions on very large caches and conserve memory on
- small caches.
-
-2006-10-13
- * Steven Grimm <sgrimm@facebook.com>: New faster hash function.
-
-2006-09-20
-
- * don't listen on UDP by default; more clear message when UDP port in use
-
-2006-09-09
- * release 1.2.0 (along with 1.1.13, which is the more tested branch)
-
- nobody has run 1.2.0 in production, to my knowledge. facebook has run
- their pre-merge-with-trunk version, but bugs were discovered (and fixed)
- after the merge. there might be more. you've been warned. :)
-
-2006-09-04
- * improved autoconf libevent detection, from the Tor project.
-
-2006-09-03
- * test suite and lot of expiration, delete, flush_all, etc corner
- case bugs fixed (Brad Fitzpatrick)
-
-2006-09-02
- * Nathan Neulinger <nneul@umr.edu>: fix breakage in expiration code
- causing expiration times to not be processed correctly.
-
-2006-08-21
- * Nathan Neulinger <nneul@umr.edu>: fix incompatabilities with
- unix domain socket support and the UDP code and clean up stale
- sockets
-
-2006-08-20
- * Nathan Neulinger <nneul@umr.edu>: unix domain socket support
-
-2006-05-03
- * Steven Grimm <sgrimm@facebook.com>: big bunch of changes:
- big CPU reduction work, UDP-based interface, increased memory
- efficiency. (intertwined patch, committed all together)
- <http://lists.danga.com/pipermail/memcached/2006-May/002164.html>
- or see svn commit logs
-
-2006-04-30
- * River Tarnell: autoconf work for Solaris 10. Brad:
- merge and verify it works on Nexenta.
-
-2006-03-04
- * avva: bucket/generation patch (old, but Brad's just finally
- committing it)
-
-2006-01-01
- * Brad Fitzpatrick <brad@danga.com>: allocate 1 slab per class
- on start-up, to avoid confusing users with out-of-memory errors
- later. this is 18 MB of allocation on start, unless max memory
- allowed with -m is lower, in which case only the smaller slab
- classes are allocated.
-
-2005-08-09
- * Elizabeth Mattijsen <liz@dijkmat.nl>: needed a way to flush all
- memcached backend servers, but not at exactly the same time (to
- reduce load peaks), I've added some simple functionality to the
- memcached protocol in the "flush_all" command that allows you to
- specify a time at which the flush will actually occur (instead of
- always at the moment the "flush_all" command is received).
-
-2005-05-25
- * patch from Peter van Dijk <peter@nextgear.nl> to make
- stderr unbuffered, for running under daemontools
-
-2005-04-04
- * patch from Don MacAskill <don@smugmug.com> 'flush_all' doesn't
- seem to work properly. Basically, if you try to add a key which
- is present, but expired, the store fails but the old key is no
- longer expired.
-
- * release 1.1.12
-
-2005-01-14
- * Date: Thu, 18 Nov 2004 15:25:59 -0600
- From: David Phillips <electrum@gmail.com>
- Here is a patch to configure.ac and Makefile.am to put the man page in
- the correct location. Trying to install the man page from a
- subdirectory results in the subdirectory being used in the install
- path (it tries to install to doc/memcached.1). This is the correct
- thing to do:
-
- - create a Makefile.am in the doc directory that installs the man page
- with man_MANS
- - modify Makefile.am in the base directory to reference the doc
- directory using SUBDIRS
- - modify the AC_CONFIG_FILES macro in configure.ac to output the
- Makefile in doc
-
-
-2005-01-14
- * pidfile saving support from Lisa Seelye <lisa@gentoo.org>, sent
- Jan 13, 2005
-
-2005-01-14
- * don't delete libevent events that haven't been added (the deltimer)
- patch from Ted Schundler <tschundler@gmail.com>
-
-2004-12-10
- * document -M and -r in manpage (Doug Porter <dsp@dsp.name>)
-
-2004-07-22
- * fix buffer overflow in items.c with 250 byte keys along with
- other info on the same line going into a 256 byte char[].
- thanks to Andrei Nigmatulin <anight@monamour.ru>
-
-2004-06-15
- * immediate deletes weren't being unlinked a few seconds,
- preventing "add" commands to the same key in that time period.
- thanks to Michael Alan Dorman <mdorman@debian.org> for the
- bug report and demo script.
-
-2004-04-30
- * released 1.1.11
-
-2004-04-24
- * Avva: Add a new command line option: -r , to maximize core file
- limit.
-
-2004-03-31
- * Avva: Use getrlimit and setrlimit to set limits for number of
- simultaneously open file descriptors. Get the current limits and
- try to raise them if they're not enough for the specified (or the
- default) setting of max connections.
-
-2004-02-24
- * Adds a '-M' flag to turn off tossing items from the cache.
- (Jason Titus <jtitus@postini.com>)
-
-2004-02-19 (Evan)
- * Install manpage on "make install", etc.
-
-2003-12-30 (Brad)
- * remove static build stuff. interferes with PAM setuid stuff
- and was only included as a possible fix with the old memory
- allocator. really shouldn't make a difference.
- * add Jay Bonci's Debian scripts and manpage
- * release version 1.1.10
-
-2003-12-01 (Avva)
- * New command: flush_all, causes all existing items to
- be invalidated immediately (without deleting them from
- memory, merely causing memcached to no longer return them).
-2003-10-23
- * Shift init code around to fix daemon mode on FreeBSD,
- * and drop root only after creating the server socket (to
- * allow the use of privileged ports)
- * version 1.1.10pre
-
-2003-10-09
- * BSD compile fixes from Ryan T. Dean
- * version 1.1.9
-
-2003-09-29
- * ignore SIGPIPE at start instead of crashing in rare cases it
- comes up. no other code had to be modified, since everything
- else is already dead-connection-aware. (avva)
-
-2003-09-09 (Avva, Lisa Marie Seelye <lisa@gentoo.org>)
- * setuid support
-
-2003-09-05 (Avva)
- * accept all new connections in the same event (so we work with ET epoll)
- * mark all items as clsid=0 after slab page reassignment to please future
- asserts (on the road to making slab page reassignment work fully)
-
-2003-08-12 (Brad Fitzpatrick)
- * use TCP_CORK on Linux or TCP_PUSH on BSD
- * only use TCP_NODELAY when we don't have alternatives
-
-2003-08-10
- * disable Nagel's Algorithm (TCP_NODELAY) for better performance (avva)
-
-2003-08-10
- * support multiple levels of verbosity (-vv)
-
-2003-08-10 (Evan Martin)
- * Makefile.am: debug, optimization, and static flags are controlled
- by the configure script.
- * configure.ac:
- - allow specifying libevent directory with --with-libevent=DIR
- - check for malloc.h (unavailable on BSDs)
- - check for socklen_t (unavailable on OSX)
- * assoc.c, items.c, slabs.c: Remove some unused headers.
- * memcached.c: allow for nonexistence of malloc.h; #define a POSIX
- macro to import mlockall flags.
-
-2003-07-29
- * version 1.1.7
- * big bug fix: item exptime 0 meant expire immediately, not never
- * version 1.1.8
-
-2003-07-22
- * make 'delete' take second arg, of time to refuse new add/replace
- * set/add/replace/delete can all take abs or delta time (delta can't
- be larger than a month)
-
-2003-07-21
- * added doc/protocol.txt
-
-2003-07-01
- * report CPU usage in stats
-
-2003-06-30
- * version 1.1.6
- * fix a number of obscure bugs
- * more stats reporting
-
-2003-06-10
- * removing use of Judy; use a hash. (judy caused memory fragmentation)
- * shrink some structures
- * security improvements
- * version 1.1.0
-
-2003-06-18
- * changing maxsize back to an unsigned int
-
-2003-06-16
- * adding PHP support
- * added CONTRIBUTORS file
- * version 1.0.4
-
-2003-06-15
- * forgot to distribute website/api (still learning auto*)
- * version 1.0.3
-
-2003-06-15
- * update to version 1.0.2
- * autoconf/automake fixes for older versions
- * make stats report version number
- * change license from GPL to BSD
-
-Fri, 13 Jun 2003 10:05:51 -0700 Evan Martin <martine@danga.com>
-
- * configure.ac, autogen.sh, Makefile.am: Use autotools.
- * items.c, memcached.c: #include <time.h> for time(),
- printf time_t as %lu (is this correct?),
- minor warnings fixes.
-
+++ /dev/null
-# Doxyfile 1.5.8
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME = memcached
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = doc/doxy
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
-# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
-# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
-# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
-# Spanish, Swedish, and Ukrainian.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES = YES
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF = YES
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it parses.
-# With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this tag.
-# The format is ext=language, where ext is a file extension, and language is one of
-# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
-# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
-# use: inc=Fortran f=C
-
-EXTENSION_MAPPING =
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen to replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING = YES
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT = NO
-
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penality.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespace are hidden.
-
-EXTRACT_ANON_NSPACES = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
-# doxygen. The layout file controls the global structure of the generated output files
-# in an output format independent way. The create the layout file that represents
-# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
-# file name after the option, if omitted DoxygenLayout.xml will be used as the name
-# of the layout file.
-
-LAYOUT_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT = .
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-
-FILE_PATTERNS = *.h *.c
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE = NO
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE =
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS = testapp.c
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = NO
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION = NO
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET =
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS = NO
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
-
-GENERATE_DOCSET = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID = org.doxygen.Project
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
-# are set, an additional index file will be generated that can be used as input for
-# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
-# HTML documentation.
-
-GENERATE_QHP = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE =
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
-# For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION =
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE = 4
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to FRAME, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
-# probably better off using the HTML help feature. Other possible values
-# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list;
-# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
-# disables this behavior completely. For backwards compatibility with previous
-# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
-# respectively.
-
-GENERATE_TREEVIEW = NONE
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH = 250
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE = 10
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT = YES
-
-# By default doxygen will write a font called FreeSans.ttf to the output
-# directory and reference it in all dot files that doxygen generates. This
-# font does not include all possible unicode characters however, so when you need
-# these (or just want a differently looking font) you can specify the font name
-# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
-# which can be done by putting it in a standard location or by setting the
-# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
-# containing the font.
-
-DOT_FONTNAME = FreeSans
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE = 10
-
-# By default doxygen will tell dot to use the output directory to look for the
-# FreeSans.ttf font (which doxygen will put there itself). If you specify a
-# different font using DOT_FONTNAME you can set the path where dot
-# can find it using this tag.
-
-DOT_FONTPATH =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH = YES
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH = YES
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
-MAX_DOT_GRAPH_DEPTH = 0
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP = YES
-
-#---------------------------------------------------------------------------
-# Options related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE = NO
+++ /dev/null
-* Hacking Memcached
-
-* Prerequisites
-
- - autoconf
- - automake
- - autotools
- - libevent
-
-* Getting Started
-
-After checking out a git repository, you must first run autogen.sh
-once in order to create the configure script.
-
-Next, run the configure script and start doing builds.
-
-* Setting up Git
-
-Though not required, there are a couple of things you can add to git
-to help development.
-
-** Pre Commit Hook
-
-The pre-commit hook can be used to ensure that your tree passes tests
-before allowing a commit. To do so, add the following to
-.git/hooks/pre-commit (which must be executable):
-
- #!/bin/sh
- make test
-
-** Post Commit Hook
-
-Because the version number changes on each commit, it's good to use a
-post commit hook to update the version number after each commit so as
-to keep the reporting accurate. To do so, add the following to
-.git/hooks/post-commit (which must be executable)
-
- #!/bin/sh
- ./version.sh
-
-** Running memcached in gdb for tests.
-
-By default `make test` will spawn a memcached daemon for each test.
-This doesn't let you easily drop into gdb or run verbosely.
-
-If you export the environment variable
-T_MEMD_USE_DAEMON="127.0.0.1:11211" the tests will use an existing
-daemon at that address.
-
-* Sending patches
-
-See current instructions at http://contributing.appspot.com/memcached
+++ /dev/null
-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.
+++ /dev/null
-bin_PROGRAMS = memcached
-pkginclude_HEADERS = protocol_binary.h
-noinst_PROGRAMS = memcached-debug sizes testapp timedrun
-
-BUILT_SOURCES=
-
-testapp_SOURCES = testapp.c util.c util.h
-
-timedrun_SOURCES = timedrun.c
-
-memcached_SOURCES = memcached.c memcached.h \
- hash.c hash.h \
- slabs.c slabs.h \
- items.c items.h \
- assoc.c assoc.h \
- thread.c daemon.c \
- stats.c stats.h \
- util.c util.h \
- trace.h cache.h sasl_defs.h
-
-if BUILD_CACHE
-memcached_SOURCES += cache.c
-testapp_SOURCES += cache.c
-endif
-
-if BUILD_SOLARIS_PRIVS
-memcached_SOURCES += solaris_priv.c
-endif
-
-if ENABLE_SASL
-memcached_SOURCES += sasl_defs.c
-endif
-
-memcached_debug_SOURCES = $(memcached_SOURCES)
-memcached_CPPFLAGS = -DNDEBUG
-memcached_debug_LDADD = @PROFILER_LDFLAGS@
-memcached_debug_CFLAGS = @PROFILER_FLAGS@
-
-memcached_LDADD =
-memcached_DEPENDENCIES =
-memcached_debug_DEPENDENCIES =
-CLEANFILES=
-
-if BUILD_DTRACE
-BUILT_SOURCES += memcached_dtrace.h
-CLEANFILES += memcached_dtrace.h
-endif
-
-if DTRACE_INSTRUMENT_OBJ
-memcached_LDADD += memcached_dtrace.o
-memcached_DEPENDENCIES += memcached_dtrace.o
-memcached_debug_LDADD += memcached_debug_dtrace.o
-memcached_debug_DEPENDENCIES += memcached_debug_dtrace.o
-CLEANFILES += memcached_dtrace.o memcached_debug_dtrace.o
-endif
-
-memcached_dtrace.h: memcached_dtrace.d
- ${DTRACE} -h -s memcached_dtrace.d
- sed -e 's,void \*,const void \*,g' memcached_dtrace.h | \
- sed -e 's,char \*,const char \*,g' | tr '\t' ' ' > mmc_dtrace.tmp
- mv mmc_dtrace.tmp memcached_dtrace.h
-
-memcached_dtrace.o: $(memcached_OBJECTS)
- $(DTRACE) $(DTRACEFLAGS) -G -o memcached_dtrace.o -s ${srcdir}/memcached_dtrace.d $(memcached_OBJECTS)
-
-memcached_debug_dtrace.o: $(memcached_debug_OBJECTS)
- $(DTRACE) $(DTRACEFLAGS) -G -o memcached_debug_dtrace.o -s ${srcdir}/memcached_dtrace.d $(memcached_debug_OBJECTS)
-
-
-SUBDIRS = doc
-DIST_DIRS = scripts
-EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d version.m4
-
-MOSTLYCLEANFILES = *.gcov *.gcno *.gcda *.tcov
-
-test: memcached-debug sizes testapp
- $(srcdir)/sizes
- $(srcdir)/testapp
- prove $(srcdir)/t
- @if test `basename $(PROFILER)` = "gcov"; then \
- for file in memcached_debug-*.gc??; do \
- mv -f $$file `echo $$file | sed 's/memcached_debug-//'`; \
- done && \
- for file in *.gcda; do \
- srcfile=`echo $$file | sed 's/.gcda/.c/'`; \
- if test -n "`echo $(memcached_debug_SOURCES) | grep $$srcfile`"; then \
- echo `$(PROFILER) $$srcfile` | sed 's/'$$srcfile':.*//'; \
- fi \
- done \
- elif test `basename $(PROFILER)` = "tcov"; then \
- files=`grep SRCFILE memcached-debug.profile/tcovd | sed 's/SRCFILE://' | sort | uniq` && \
- $(PROFILER) -x memcached-debug.profile $$files 2>&1; \
- for file in *.tcov; do \
- srcfile=`echo $$file | sed 's/.tcov//'`; \
- if test -n "`echo $(memcached_debug_SOURCES) | grep $$srcfile`"; then \
- echo $$srcfile : `grep 'Percent of the file executed' $$file`; \
- fi \
- done \
- else :; fi
-
-docs:
- (cat Doxyfile ; echo "PROJECT_NUMBER=`cat version.num`") | doxygen -
-
-dist-hook:
- rm -f $(distdir)/*/*~ $(distdir)/t/lib/*~ $(distdir)/*~
+++ /dev/null
-http://www.danga.com/memcached/news.bml
+++ /dev/null
-# Memcached
-
-## Dependencies
-
-* libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev)
-
-## Environment
-
-### Linux
-
-If using Linux, you need a kernel with epoll. Sure, libevent will
-work with normal select, but it sucks.
-
-epoll isn't in Linux 2.4, but there's a backport at:
-
- http://www.xmailserver.org/linux-patches/nio-improve.html
-
-You want the epoll-lt patch (level-triggered).
-
-### Mac OS X
-
-If you're using MacOS, you'll want libevent 1.1 or higher to deal with
-a kqueue bug.
-
-Also, be warned that the -k (mlockall) option to memcached might be
-dangerous when using a large cache. Just make sure the memcached machines
-don't swap. memcached does non-blocking network I/O, but not disk. (it
-should never go to disk, or you've lost the whole point of it)
-
-## Website
-
-* http://www.memcached.org
-
-## Contributing
-
-Want to contribute? Up-to-date pointers should be at:
-
-* http://contributing.appspot.com/memcached
+++ /dev/null
-/* -*- 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:
- * <http://burtleburtle.net/bob/hash/doobs.html>
- * "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
- * You may use this code any way you wish, private, educational,
- * or commercial. It's free."
- *
- * The rest of the file is licensed under the BSD license. See LICENSE.
- */
-
-#include "memcached.h"
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/signal.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <pthread.h>
-
-static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER;
-
-
-typedef unsigned long int ub4; /* unsigned 4-byte quantities */
-typedef unsigned char ub1; /* unsigned 1-byte quantities */
-
-/* how many powers of 2's worth of buckets we use */
-static unsigned int hashpower = HASHPOWER_DEFAULT;
-
-#define hashsize(n) ((ub4)1<<(n))
-#define hashmask(n) (hashsize(n)-1)
-
-/* Main hash table. This is where we look except during expansion. */
-static item** primary_hashtable = 0;
-
-/*
- * Previous hash table. During expansion, we look here for keys that haven't
- * been moved over to the primary yet.
- */
-static item** old_hashtable = 0;
-
-/* Number of items in the hash table. */
-static unsigned int hash_items = 0;
-
-/* Flag: Are we in the middle of expanding now? */
-static bool expanding = false;
-
-/*
- * During expansion we migrate values with bucket granularity; this is how
- * far we've gotten so far. Ranges from 0 .. hashsize(hashpower - 1) - 1.
- */
-static unsigned int expand_bucket = 0;
-
-void assoc_init(const int hashtable_init) {
- if (hashtable_init) {
- hashpower = hashtable_init;
- }
- primary_hashtable = calloc(hashsize(hashpower), sizeof(void *));
- if (! primary_hashtable) {
- fprintf(stderr, "Failed to init hashtable.\n");
- exit(EXIT_FAILURE);
- }
- STATS_LOCK();
- stats.hash_power_level = hashpower;
- stats.hash_bytes = hashsize(hashpower) * sizeof(void *);
- STATS_UNLOCK();
-}
-
-item *assoc_find(const char *key, const size_t nkey, const uint32_t hv) {
- item *it;
- unsigned int oldbucket;
-
- if (expanding &&
- (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)
- {
- it = old_hashtable[oldbucket];
- } else {
- it = primary_hashtable[hv & hashmask(hashpower)];
- }
-
- item *ret = NULL;
- int depth = 0;
- while (it) {
- if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == 0)) {
- ret = it;
- break;
- }
- it = it->h_next;
- ++depth;
- }
- MEMCACHED_ASSOC_FIND(key, nkey, depth);
- return ret;
-}
-
-/* returns the address of the item pointer before the key. if *item == 0,
- the item wasn't found */
-
-static item** _hashitem_before (const char *key, const size_t nkey, const uint32_t hv) {
- item **pos;
- unsigned int oldbucket;
-
- if (expanding &&
- (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)
- {
- pos = &old_hashtable[oldbucket];
- } else {
- pos = &primary_hashtable[hv & hashmask(hashpower)];
- }
-
- while (*pos && ((nkey != (*pos)->nkey) || memcmp(key, ITEM_key(*pos), nkey))) {
- pos = &(*pos)->h_next;
- }
- return pos;
-}
-
-/* grows the hashtable to the next power of 2. */
-static void assoc_expand(void) {
- old_hashtable = primary_hashtable;
-
- primary_hashtable = calloc(hashsize(hashpower + 1), sizeof(void *));
- if (primary_hashtable) {
- if (settings.verbose > 1)
- fprintf(stderr, "Hash table expansion starting\n");
- hashpower++;
- expanding = true;
- expand_bucket = 0;
- STATS_LOCK();
- stats.hash_power_level = hashpower;
- stats.hash_bytes += hashsize(hashpower) * sizeof(void *);
- stats.hash_is_expanding = 1;
- STATS_UNLOCK();
- pthread_cond_signal(&maintenance_cond);
- } else {
- primary_hashtable = old_hashtable;
- /* Bad news, but we can keep running. */
- }
-}
-
-/* Note: this isn't an assoc_update. The key must not already exist to call this */
-int assoc_insert(item *it, const uint32_t hv) {
- unsigned int oldbucket;
-
-// assert(assoc_find(ITEM_key(it), it->nkey) == 0); /* shouldn't have duplicately named things defined */
-
- if (expanding &&
- (oldbucket = (hv & hashmask(hashpower - 1))) >= expand_bucket)
- {
- it->h_next = old_hashtable[oldbucket];
- old_hashtable[oldbucket] = it;
- } else {
- it->h_next = primary_hashtable[hv & hashmask(hashpower)];
- primary_hashtable[hv & hashmask(hashpower)] = it;
- }
-
- hash_items++;
- if (! expanding && hash_items > (hashsize(hashpower) * 3) / 2) {
- assoc_expand();
- }
-
- MEMCACHED_ASSOC_INSERT(ITEM_key(it), it->nkey, hash_items);
- return 1;
-}
-
-void assoc_delete(const char *key, const size_t nkey, const uint32_t hv) {
- item **before = _hashitem_before(key, nkey, hv);
-
- if (*before) {
- item *nxt;
- hash_items--;
- /* The DTrace probe cannot be triggered as the last instruction
- * due to possible tail-optimization by the compiler
- */
- MEMCACHED_ASSOC_DELETE(key, nkey, hash_items);
- nxt = (*before)->h_next;
- (*before)->h_next = 0; /* probably pointless, but whatever. */
- *before = nxt;
- return;
- }
- /* Note: we never actually get here. the callers don't delete things
- they can't find. */
- assert(*before != 0);
-}
-
-
-static volatile int do_run_maintenance_thread = 1;
-
-#define DEFAULT_HASH_BULK_MOVE 1
-int hash_bulk_move = DEFAULT_HASH_BULK_MOVE;
-
-static void *assoc_maintenance_thread(void *arg) {
-
- while (do_run_maintenance_thread) {
- int ii = 0;
-
- /* Lock the cache, and bulk move multiple buckets to the new
- * hash table. */
- mutex_lock(&cache_lock);
-
- for (ii = 0; ii < hash_bulk_move && expanding; ++ii) {
- item *it, *next;
- int bucket;
-
- for (it = old_hashtable[expand_bucket]; NULL != it; it = next) {
- next = it->h_next;
-
- bucket = hash(ITEM_key(it), it->nkey, 0) & hashmask(hashpower);
- it->h_next = primary_hashtable[bucket];
- primary_hashtable[bucket] = it;
- }
-
- old_hashtable[expand_bucket] = NULL;
-
- expand_bucket++;
- if (expand_bucket == hashsize(hashpower - 1)) {
- expanding = false;
- free(old_hashtable);
- STATS_LOCK();
- stats.hash_bytes -= hashsize(hashpower - 1) * sizeof(void *);
- stats.hash_is_expanding = 0;
- STATS_UNLOCK();
- if (settings.verbose > 1)
- fprintf(stderr, "Hash table expansion done\n");
- }
- }
-
- if (!expanding) {
- /* We are done expanding.. just wait for next invocation */
- pthread_cond_wait(&maintenance_cond, &cache_lock);
- }
-
- mutex_unlock(&cache_lock);
- }
- return NULL;
-}
-
-static pthread_t maintenance_tid;
-
-int start_assoc_maintenance_thread() {
- int ret;
- char *env = getenv("MEMCACHED_HASH_BULK_MOVE");
- if (env != NULL) {
- hash_bulk_move = atoi(env);
- if (hash_bulk_move == 0) {
- hash_bulk_move = DEFAULT_HASH_BULK_MOVE;
- }
- }
- if ((ret = pthread_create(&maintenance_tid, NULL,
- assoc_maintenance_thread, NULL)) != 0) {
- fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
- return -1;
- }
- return 0;
-}
-
-void stop_assoc_maintenance_thread() {
- mutex_lock(&cache_lock);
- do_run_maintenance_thread = 0;
- pthread_cond_signal(&maintenance_cond);
- mutex_unlock(&cache_lock);
-
- /* Wait for the maintenance thread to stop */
- pthread_join(maintenance_tid, NULL);
-}
-
-
+++ /dev/null
-/* 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);
-
+++ /dev/null
-#!/bin/sh
-
-# Get the initial version.
-perl version.pl
-
-die() {
- echo "$@"
- exit 1
-}
-
-# Try to locate a program by using which, and verify that the file is an
-# executable
-locate_binary() {
- for f in $@
- do
- file=`which $f 2>/dev/null | grep -v '^no '`
- if test -n "$file" -a -x "$file"; then
- echo $file
- return 0
- fi
- done
-
- echo ""
- return 1
-}
-
-echo "aclocal..."
-if test x$ACLOCAL = x; then
- ACLOCAL=`locate_binary aclocal-1.11 aclocal-1.10 aclocal-1.9 aclocal19 aclocal-1.7 aclocal17 aclocal-1.5 aclocal15 aclocal`
- if test x$ACLOCAL = x; then
- die "Did not find a supported aclocal"
- fi
-fi
-$ACLOCAL || exit 1
-
-echo "autoheader..."
-AUTOHEADER=${AUTOHEADER:-autoheader}
-$AUTOHEADER || exit 1
-
-echo "automake..."
-if test x$AUTOMAKE = x; then
- AUTOMAKE=`locate_binary automake-1.12 automake-1.11 automake-1.10 automake-1.9 automake-1.7`
- if test x$AUTOMAKE = x; then
- die "Did not find a supported automake"
- fi
-fi
-$AUTOMAKE --foreign --add-missing || $AUTOMAKE --gnu --add-missing || exit 1
-
-echo "autoconf..."
-AUTOCONF=${AUTOCONF:-autoconf}
-$AUTOCONF || exit 1
-
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#include <config.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-#include <string.h>
-
-#ifndef NDEBUG
-#include <signal.h>
-#endif
-
-#include "cache.h"
-
-#ifndef NDEBUG
-const uint64_t redzone_pattern = 0xdeadbeefcafebabe;
-int cache_error = 0;
-#endif
-
-const int initial_pool_size = 64;
-
-cache_t* cache_create(const char *name, size_t bufsize, size_t align,
- cache_constructor_t* constructor,
- cache_destructor_t* destructor) {
- cache_t* ret = calloc(1, sizeof(cache_t));
- char* nm = strdup(name);
- void** ptr = calloc(initial_pool_size, sizeof(void*));
- if (ret == NULL || nm == NULL || ptr == NULL ||
- pthread_mutex_init(&ret->mutex, NULL) == -1) {
- free(ret);
- free(nm);
- free(ptr);
- return NULL;
- }
-
- ret->name = nm;
- ret->ptr = ptr;
- ret->freetotal = initial_pool_size;
- ret->constructor = constructor;
- ret->destructor = destructor;
-
-#ifndef NDEBUG
- ret->bufsize = bufsize + 2 * sizeof(redzone_pattern);
-#else
- ret->bufsize = bufsize;
-#endif
-
- return ret;
-}
-
-static inline void* get_object(void *ptr) {
-#ifndef NDEBUG
- uint64_t *pre = ptr;
- return pre + 1;
-#else
- return ptr;
-#endif
-}
-
-void cache_destroy(cache_t *cache) {
- while (cache->freecurr > 0) {
- void *ptr = cache->ptr[--cache->freecurr];
- if (cache->destructor) {
- cache->destructor(get_object(ptr), NULL);
- }
- free(ptr);
- }
- free(cache->name);
- free(cache->ptr);
- pthread_mutex_destroy(&cache->mutex);
- free(cache);
-}
-
-void* cache_alloc(cache_t *cache) {
- void *ret;
- void *object;
- pthread_mutex_lock(&cache->mutex);
- if (cache->freecurr > 0) {
- ret = cache->ptr[--cache->freecurr];
- object = get_object(ret);
- } else {
- object = ret = malloc(cache->bufsize);
- if (ret != NULL) {
- object = get_object(ret);
-
- if (cache->constructor != NULL &&
- cache->constructor(object, NULL, 0) != 0) {
- free(ret);
- object = NULL;
- }
- }
- }
- pthread_mutex_unlock(&cache->mutex);
-
-#ifndef NDEBUG
- if (object != NULL) {
- /* add a simple form of buffer-check */
- uint64_t *pre = ret;
- *pre = redzone_pattern;
- ret = pre+1;
- memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)),
- &redzone_pattern, sizeof(redzone_pattern));
- }
-#endif
-
- return object;
-}
-
-void cache_free(cache_t *cache, void *ptr) {
- pthread_mutex_lock(&cache->mutex);
-
-#ifndef NDEBUG
- /* validate redzone... */
- if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)),
- &redzone_pattern, sizeof(redzone_pattern)) != 0) {
- raise(SIGABRT);
- cache_error = 1;
- pthread_mutex_unlock(&cache->mutex);
- return;
- }
- uint64_t *pre = ptr;
- --pre;
- if (*pre != redzone_pattern) {
- raise(SIGABRT);
- cache_error = -1;
- pthread_mutex_unlock(&cache->mutex);
- return;
- }
- ptr = pre;
-#endif
- if (cache->freecurr < cache->freetotal) {
- cache->ptr[cache->freecurr++] = ptr;
- } else {
- /* try to enlarge free connections array */
- size_t newtotal = cache->freetotal * 2;
- void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal);
- if (new_free) {
- cache->freetotal = newtotal;
- cache->ptr = new_free;
- cache->ptr[cache->freecurr++] = ptr;
- } else {
- if (cache->destructor) {
- cache->destructor(ptr, NULL);
- }
- free(ptr);
-
- }
- }
- pthread_mutex_unlock(&cache->mutex);
-}
-
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#ifndef CACHE_H
-#define CACHE_H
-#include <pthread.h>
-
-#ifdef HAVE_UMEM_H
-#include <umem.h>
-#define cache_t umem_cache_t
-#define cache_alloc(a) umem_cache_alloc(a, UMEM_DEFAULT)
-#define cache_free(a, b) umem_cache_free(a, b)
-#define cache_create(a,b,c,d,e) umem_cache_create((char*)a, b, c, d, e, NULL, NULL, NULL, 0)
-#define cache_destroy(a) umem_cache_destroy(a);
-
-#else
-
-#ifndef NDEBUG
-/* may be used for debug purposes */
-extern int cache_error;
-#endif
-
-/**
- * Constructor used to initialize allocated objects
- *
- * @param obj pointer to the object to initialized.
- * @param notused1 This parameter is currently not used.
- * @param notused2 This parameter is currently not used.
- * @return you should return 0, but currently this is not checked
- */
-typedef int cache_constructor_t(void* obj, void* notused1, int notused2);
-/**
- * Destructor used to clean up allocated objects before they are
- * returned to the operating system.
- *
- * @param obj pointer to the object to initialized.
- * @param notused1 This parameter is currently not used.
- * @param notused2 This parameter is currently not used.
- * @return you should return 0, but currently this is not checked
- */
-typedef void cache_destructor_t(void* obj, void* notused);
-
-/**
- * Definition of the structure to keep track of the internal details of
- * the cache allocator. Touching any of these variables results in
- * undefined behavior.
- */
-typedef struct {
- /** Mutex to protect access to the structure */
- pthread_mutex_t mutex;
- /** Name of the cache objects in this cache (provided by the caller) */
- char *name;
- /** List of pointers to available buffers in this cache */
- void **ptr;
- /** The size of each element in this cache */
- size_t bufsize;
- /** The capacity of the list of elements */
- int freetotal;
- /** The current number of free elements */
- int freecurr;
- /** The constructor to be called each time we allocate more memory */
- cache_constructor_t* constructor;
- /** The destructor to be called each time before we release memory */
- cache_destructor_t* destructor;
-} cache_t;
-
-/**
- * Create an object cache.
- *
- * The object cache will let you allocate objects of the same size. It is fully
- * MT safe, so you may allocate objects from multiple threads without having to
- * do any syncrhonization in the application code.
- *
- * @param name the name of the object cache. This name may be used for debug purposes
- * and may help you track down what kind of object you have problems with
- * (buffer overruns, leakage etc)
- * @param bufsize the size of each object in the cache
- * @param align the alignment requirements of the objects in the cache.
- * @param constructor the function to be called to initialize memory when we need
- * to allocate more memory from the os.
- * @param destructor the function to be called before we release the memory back
- * to the os.
- * @return a handle to an object cache if successful, NULL otherwise.
- */
-cache_t* cache_create(const char* name, size_t bufsize, size_t align,
- cache_constructor_t* constructor,
- cache_destructor_t* destructor);
-/**
- * Destroy an object cache.
- *
- * Destroy and invalidate an object cache. You should return all buffers allocated
- * with cache_alloc by using cache_free before calling this function. Not doing
- * so results in undefined behavior (the buffers may or may not be invalidated)
- *
- * @param handle the handle to the object cache to destroy.
- */
-void cache_destroy(cache_t* handle);
-/**
- * Allocate an object from the cache.
- *
- * @param handle the handle to the object cache to allocate from
- * @return a pointer to an initialized object from the cache, or NULL if
- * the allocation cannot be satisfied.
- */
-void* cache_alloc(cache_t* handle);
-/**
- * Return an object back to the cache.
- *
- * The caller should return the object in an initialized state so that
- * the object may be returned in an expected state from cache_alloc.
- *
- * @param handle handle to the object cache to return the object to
- * @param ptr pointer to the object to return.
- */
-void cache_free(cache_t* handle, void* ptr);
-#endif
-
-#endif
+++ /dev/null
-AC_PREREQ(2.52)
-m4_include([version.m4])
-m4_include([m4/c99-backport.m4])
-AC_INIT(memcached, VERSION_NUMBER, memcached@googlegroups.com)
-AC_CANONICAL_SYSTEM
-AC_CONFIG_SRCDIR(memcached.c)
-AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
-AM_CONFIG_HEADER(config.h)
-
-AC_PROG_CC
-
-dnl **********************************************************************
-dnl DETECT_ICC ([ACTION-IF-YES], [ACTION-IF-NO])
-dnl
-dnl check if this is the Intel ICC compiler, and if so run the ACTION-IF-YES
-dnl sets the $ICC variable to "yes" or "no"
-dnl **********************************************************************
-AC_DEFUN([DETECT_ICC],
-[
- ICC="no"
- AC_MSG_CHECKING([for icc in use])
- if test "$GCC" = "yes"; then
- dnl check if this is icc acting as gcc in disguise
- AC_EGREP_CPP([^__INTEL_COMPILER], [__INTEL_COMPILER],
- AC_MSG_RESULT([no])
- [$2],
- AC_MSG_RESULT([yes])
- [$1]
- ICC="yes")
- else
- AC_MSG_RESULT([no])
- [$2]
- fi
-])
-
-DETECT_ICC([], [])
-
-dnl **********************************************************************
-dnl DETECT_SUNCC ([ACTION-IF-YES], [ACTION-IF-NO])
-dnl
-dnl check if this is the Sun Studio compiler, and if so run the ACTION-IF-YES
-dnl sets the $SUNCC variable to "yes" or "no"
-dnl **********************************************************************
-AC_DEFUN([DETECT_SUNCC],
-[
- AC_CHECK_DECL([__SUNPRO_C], [SUNCC="yes"], [SUNCC="no"])
- AS_IF(test "x$SUNCC" = "xyes", [$1], [$2])
-
-])
-
-DETECT_SUNCC([CFLAGS="-mt $CFLAGS"], [])
-
-if test "$ICC" = "no"; then
- AC_PROG_CC_C99
-fi
-
-AM_PROG_CC_C_O
-AC_PROG_INSTALL
-
-AC_ARG_ENABLE(sasl,
- [AS_HELP_STRING([--enable-sasl],[Enable SASL authentication])])
-
-AC_ARG_ENABLE(sasl_pwdb,
- [AS_HELP_STRING([--enable-sasl-pwdb],[Enable plaintext password db])])
-
-AS_IF([test "x$enable_sasl_pwdb" = "xyes"],
- [enable_sasl=yes ])
-
-
-dnl **********************************************************************
-dnl DETECT_SASL_CB_GETCONF
-dnl
-dnl check if we can use SASL_CB_GETCONF
-dnl **********************************************************************
-AC_DEFUN([AC_C_DETECT_SASL_CB_GETCONF],
-[
- AC_CACHE_CHECK([for SASL_CB_GETCONF],
- [ac_cv_c_sasl_cb_getconf],
- [AC_TRY_COMPILE(
- [
-#include <sasl/sasl.h>
- ], [
-unsigned long val = SASL_CB_GETCONF;
- ],
- [ ac_cv_c_sasl_cb_getconf=yes ],
- [ ac_cv_c_sasl_cb_getconf=no ])
- ])
- AS_IF([test "$ac_cv_c_sasl_cb_getconf" = "yes"],
- [AC_DEFINE([HAVE_SASL_CB_GETCONF], 1,
- [Set to nonzero if your SASL implementation supports SASL_CB_GETCONF])])
-])
-
-AC_CHECK_HEADERS([sasl/sasl.h])
-if test "x$enable_sasl" = "xyes"; then
- AC_C_DETECT_SASL_CB_GETCONF
- AC_DEFINE([ENABLE_SASL],1,[Set to nonzero if you want to include SASL])
- AC_SEARCH_LIBS([sasl_server_init], [sasl2 sasl], [],
- [
- AC_MSG_ERROR([Failed to locate the library containing sasl_server_init])
- ])
-
- AS_IF([test "x$enable_sasl_pwdb" = "xyes"],
- [AC_DEFINE([ENABLE_SASL_PWDB], 1,
- [Set to nonzero if you want to enable a SASL pwdb])])
-fi
-
-AC_ARG_ENABLE(dtrace,
- [AS_HELP_STRING([--enable-dtrace],[Enable dtrace probes])])
-if test "x$enable_dtrace" = "xyes"; then
- AC_PATH_PROG([DTRACE], [dtrace], "no", [/usr/sbin:$PATH])
- if test "x$DTRACE" != "xno"; then
- AC_DEFINE([ENABLE_DTRACE],1,[Set to nonzero if you want to include DTRACE])
- build_dtrace=yes
- # DTrace on MacOSX does not use -G option
- $DTRACE -G -o conftest.$$ -s memcached_dtrace.d 2>/dev/zero
- if test $? -eq 0
- then
- dtrace_instrument_obj=yes
- rm conftest.$$
- fi
-
- if test "`which tr`" = "/usr/ucb/tr"; then
- AC_MSG_ERROR([Please remove /usr/ucb from your path. See man standards for more info])
- fi
- else
- AC_MSG_ERROR([Need dtrace binary and OS support.])
- fi
-fi
-
-AM_CONDITIONAL([BUILD_DTRACE],[test "$build_dtrace" = "yes"])
-AM_CONDITIONAL([DTRACE_INSTRUMENT_OBJ],[test "$dtrace_instrument_obj" = "yes"])
-AM_CONDITIONAL([ENABLE_SASL],[test "$enable_sasl" = "yes"])
-
-AC_SUBST(DTRACE)
-AC_SUBST(DTRACEFLAGS)
-AC_SUBST(ENABLE_SASL)
-AC_SUBST(PROFILER_LDFLAGS)
-
-AC_ARG_ENABLE(coverage,
- [AS_HELP_STRING([--disable-coverage],[Disable code coverage])])
-
-if test "x$enable_coverage" != "xno"; then
- if test "$ICC" = "yes"
- then
- dnl ICC trying to be gcc, but not well
- CFLAGS="$CFLAGS -pthread"
- elif test "$GCC" = "yes"
- then
- CFLAGS="$CFLAGS -pthread"
- AC_PATH_PROG([PROFILER], [gcov], "no", [$PATH])
- if test "x$PROFILER" != "xno"; then
- # Issue 97: The existense of gcov doesn't mean we have -lgcov
- AC_CHECK_LIB(gcov, main,
- [
- PROFILER_FLAGS="-fprofile-arcs -ftest-coverage"
- PROFILER_LDFLAGS="-lgcov"
- ], [
- PROFILER_FLAGS=
- PROFILER_LDFLAGS=
- ])
- fi
- elif test "$SUNCC" = "yes"
- then
- AC_PATH_PROG([PROFILER], [tcov], "no", [$PATH])
- if test "x$PROFILER" != "xno"; then
- PROFILER_FLAGS=-xprofile=tcov
- fi
- fi
-fi
-AC_SUBST(PROFILER_FLAGS)
-
-
-AC_ARG_ENABLE(64bit,
- [AS_HELP_STRING([--enable-64bit],[build 64bit version])])
-if test "x$enable_64bit" = "xyes"
-then
- org_cflags=$CFLAGS
- CFLAGS=-m64
- AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([], [dnl
-return sizeof(void*) == 8 ? 0 : 1;
- ])
- ],[
- CFLAGS="-m64 $org_cflags"
- ],[
- AC_MSG_ERROR([Don't know how to build a 64-bit object.])
- ])
-fi
-
-# Issue 213: Search for clock_gettime to help people linking
-# with a static version of libevent
-AC_SEARCH_LIBS(clock_gettime, rt)
-# Issue 214: Search for the network libraries _before_ searching
-# for libevent (to help people linking with static libevent)
-AC_SEARCH_LIBS(socket, socket)
-AC_SEARCH_LIBS(gethostbyname, nsl)
-
-trylibeventdir=""
-AC_ARG_WITH(libevent,
- [ --with-libevent=PATH Specify path to libevent installation ],
- [
- if test "x$withval" != "xno" ; then
- trylibeventdir=$withval
- fi
- ]
-)
-
-dnl ------------------------------------------------------
-dnl libevent detection. swiped from Tor. modified a bit.
-
-LIBEVENT_URL=http://www.monkey.org/~provos/libevent/
-
-AC_CACHE_CHECK([for libevent directory], ac_cv_libevent_dir, [
- saved_LIBS="$LIBS"
- saved_LDFLAGS="$LDFLAGS"
- saved_CPPFLAGS="$CPPFLAGS"
- le_found=no
- for ledir in $trylibeventdir "" $prefix /usr/local ; do
- LDFLAGS="$saved_LDFLAGS"
- LIBS="-levent $saved_LIBS"
-
- # Skip the directory if it isn't there.
- if test ! -z "$ledir" -a ! -d "$ledir" ; then
- continue;
- fi
- if test ! -z "$ledir" ; then
- if test -d "$ledir/lib" ; then
- LDFLAGS="-L$ledir/lib $LDFLAGS"
- else
- LDFLAGS="-L$ledir $LDFLAGS"
- fi
- if test -d "$ledir/include" ; then
- CPPFLAGS="-I$ledir/include $CPPFLAGS"
- else
- CPPFLAGS="-I$ledir $CPPFLAGS"
- fi
- fi
- # Can I compile and link it?
- AC_TRY_LINK([#include <sys/time.h>
-#include <sys/types.h>
-#include <event.h>], [ event_init(); ],
- [ libevent_linked=yes ], [ libevent_linked=no ])
- if test $libevent_linked = yes; then
- if test ! -z "$ledir" ; then
- ac_cv_libevent_dir=$ledir
- _myos=`echo $target_os | cut -f 1 -d .`
- AS_IF(test "$SUNCC" = "yes" -o "x$_myos" = "xsolaris2",
- [saved_LDFLAGS="$saved_LDFLAGS -Wl,-R$ledir/lib"],
- [AS_IF(test "$GCC" = "yes",
- [saved_LDFLAGS="$saved_LDFLAGS -Wl,-rpath,$ledir/lib"])])
- else
- ac_cv_libevent_dir="(system)"
- fi
- le_found=yes
- break
- fi
- done
- LIBS="$saved_LIBS"
- LDFLAGS="$saved_LDFLAGS"
- CPPFLAGS="$saved_CPPFLAGS"
- if test $le_found = no ; then
- AC_MSG_ERROR([libevent is required. You can get it from $LIBEVENT_URL
-
- If it's already installed, specify its path using --with-libevent=/dir/
-])
- fi
-])
-LIBS="-levent $LIBS"
-if test $ac_cv_libevent_dir != "(system)"; then
- if test -d "$ac_cv_libevent_dir/lib" ; then
- LDFLAGS="-L$ac_cv_libevent_dir/lib $LDFLAGS"
- le_libdir="$ac_cv_libevent_dir/lib"
- else
- LDFLAGS="-L$ac_cv_libevent_dir $LDFLAGS"
- le_libdir="$ac_cv_libevent_dir"
- fi
- if test -d "$ac_cv_libevent_dir/include" ; then
- CPPFLAGS="-I$ac_cv_libevent_dir/include $CPPFLAGS"
- else
- CPPFLAGS="-I$ac_cv_libevent_dir $CPPFLAGS"
- fi
-fi
-
-dnl ----------------------------------------------------------------------------
-
-AC_SEARCH_LIBS(umem_cache_create, umem)
-AC_SEARCH_LIBS(gethugepagesizes, hugetlbfs)
-
-AC_HEADER_STDBOOL
-AH_BOTTOM([#if HAVE_STDBOOL_H
-#include <stdbool.h>
-#else
-#define bool char
-#define false 0
-#define true 1
-#endif ])
-
-AC_CHECK_HEADERS([inttypes.h])
-AH_BOTTOM([#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-])
-
-dnl **********************************************************************
-dnl Figure out if this system has the stupid sasl_callback_ft
-dnl **********************************************************************
-
-AC_DEFUN([AC_HAVE_SASL_CALLBACK_FT],
-[AC_CACHE_CHECK(for sasl_callback_ft, ac_cv_has_sasl_callback_ft,
-[
- AC_TRY_COMPILE([
- #ifdef HAVE_SASL_SASL_H
- #include <sasl/sasl.h>
- #include <sasl/saslplug.h>
- #endif
- ],[
- sasl_callback_ft a_callback;
- ],[
- ac_cv_has_sasl_callback_ft=yes
- ],[
- ac_cv_has_sasl_callback_ft=no
- ])
-])
-if test $ac_cv_has_sasl_callback_ft = yes; then
- AC_DEFINE(HAVE_SASL_CALLBACK_FT, 1, [we have sasl_callback_ft])
-fi
-])
-
-AC_HAVE_SASL_CALLBACK_FT
-
-dnl **********************************************************************
-dnl DETECT_UINT64_SUPPORT
-dnl
-dnl check if we can use a uint64_t
-dnl **********************************************************************
-AC_DEFUN([AC_C_DETECT_UINT64_SUPPORT],
-[
- AC_CACHE_CHECK([for print macros for integers (C99 section 7.8.1)],
- [ac_cv_c_uint64_support],
- [AC_TRY_COMPILE(
- [
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-#include <stdio.h>
- ], [
- uint64_t val = 0;
- fprintf(stderr, "%" PRIu64 "\n", val);
- ],
- [ ac_cv_c_uint64_support=yes ],
- [ ac_cv_c_uint64_support=no ])
- ])
-])
-
-AC_C_DETECT_UINT64_SUPPORT
-AS_IF([test "x$ac_cv_c_uint64_support" = "xno"],
- [AC_MSG_WARN([
-
-Failed to use print macros (PRIu) as defined in C99 section 7.8.1.
-
-])])
-
-AC_C_CONST
-
-dnl From licq: Copyright (c) 2000 Dirk Mueller
-dnl Check if the type socklen_t is defined anywhere
-AC_DEFUN([AC_C_SOCKLEN_T],
-[AC_CACHE_CHECK(for socklen_t, ac_cv_c_socklen_t,
-[
- AC_TRY_COMPILE([
- #include <sys/types.h>
- #include <sys/socket.h>
- ],[
- socklen_t foo;
- ],[
- ac_cv_c_socklen_t=yes
- ],[
- ac_cv_c_socklen_t=no
- ])
-])
-if test $ac_cv_c_socklen_t = no; then
- AC_DEFINE(socklen_t, int, [define to int if socklen_t not available])
-fi
-])
-
-AC_C_SOCKLEN_T
-
-dnl Check if we're a little-endian or a big-endian system, needed by hash code
-AC_DEFUN([AC_C_ENDIAN],
-[AC_CACHE_CHECK(for endianness, ac_cv_c_endian,
-[
- AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([], [dnl
- long val = 1;
- char *c = (char *) &val;
- exit(*c == 1);
- ])
- ],[
- ac_cv_c_endian=big
- ],[
- ac_cv_c_endian=little
- ])
-])
-if test $ac_cv_c_endian = big; then
- AC_DEFINE(ENDIAN_BIG, 1, [machine is bigendian])
-fi
-if test $ac_cv_c_endian = little; then
- AC_DEFINE(ENDIAN_LITTLE, 1, [machine is littleendian])
-fi
-])
-
-AC_C_ENDIAN
-
-AC_DEFUN([AC_C_HTONLL],
-[
- AC_MSG_CHECKING([for htonll])
- have_htoll="no"
- AC_TRY_LINK([
-#include <sys/types.h>
-#include <netinet/in.h>
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h> */
-#endif
- ], [
- return htonll(0);
- ], [
- have_htoll="yes"
- AC_DEFINE([HAVE_HTONLL], [1], [Have ntohll])
- ], [
- have_htoll="no"
- ])
-
- AC_MSG_RESULT([$have_htoll])
-])
-
-AC_C_HTONLL
-
-dnl Check whether the user's system supports pthread
-AC_SEARCH_LIBS(pthread_create, pthread)
-if test "x$ac_cv_search_pthread_create" = "xno"; then
- AC_MSG_ERROR([Can't enable threads without the POSIX thread library.])
-fi
-
-AC_CHECK_FUNCS(mlockall)
-AC_CHECK_FUNCS(getpagesizes)
-AC_CHECK_FUNCS(memcntl)
-AC_CHECK_FUNCS(sigignore)
-AC_CHECK_FUNCS(clock_gettime)
-
-AC_DEFUN([AC_C_ALIGNMENT],
-[AC_CACHE_CHECK(for alignment, ac_cv_c_alignment,
-[
- AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([
-#include <stdlib.h>
-#include <inttypes.h>
- ], [
- char *buf = malloc(32);
-
- uint64_t *ptr = (uint64_t*)(buf+2);
- // catch sigbus, etc.
- *ptr = 0x1;
-
- // catch unaligned word access (ARM cpus)
- *buf = 1; *(buf +1) = 2; *(buf + 2) = 2; *(buf + 3) = 3; *(buf + 4) = 4;
- int* i = (int*)(buf+1);
- return (84148994 == i) ? 0 : 1;
- ])
- ],[
- ac_cv_c_alignment=none
- ],[
- ac_cv_c_alignment=need
- ],[
- ac_cv_c_alignment=need
- ])
-])
-if test $ac_cv_c_alignment = need; then
- AC_DEFINE(NEED_ALIGN, 1, [Machine need alignment])
-fi
-])
-
-AC_C_ALIGNMENT
-
-dnl Check for our specific usage of GCC atomics.
-dnl These were added in 4.1.2, but 32bit OS's may lack shorts and 4.1.2
-dnl lacks testable defines.
-have_gcc_atomics=no
-AC_MSG_CHECKING(for GCC atomics)
-AC_TRY_LINK([],[
- unsigned short a;
- unsigned short b;
- b = __sync_add_and_fetch(&a, 1);
- b = __sync_sub_and_fetch(&a, 2);
- ],[have_gcc_atomics=yes
- AC_DEFINE(HAVE_GCC_ATOMICS, 1, [GCC Atomics available])])
-AC_MSG_RESULT($have_gcc_atomics)
-
-dnl Check for the requirements for running memcached with less privileges
-dnl than the default privilege set. On Solaris we need setppriv and priv.h
-dnl If you want to add support for other platforms you should check for
-dnl your requirements, define HAVE_DROP_PRIVILEGES, and make sure you add
-dnl the source file containing the implementation into memcached_SOURCE
-dnl in Makefile.am
-AC_CHECK_FUNCS(setppriv, [
- AC_CHECK_HEADER(priv.h, [
- AC_DEFINE([HAVE_DROP_PRIVILEGES], 1,
- [Define this if you have an implementation of drop_privileges()])
- build_solaris_privs=yes
- ], [])
-],[])
-
-AM_CONDITIONAL([BUILD_SOLARIS_PRIVS],[test "$build_solaris_privs" = "yes"])
-
-AC_CHECK_HEADER(umem.h, [
- AC_DEFINE([HAVE_UMEM_H], 1,
- [Define this if you have umem.h])
- build_cache=no
-], [build_cache=yes])
-
-AM_CONDITIONAL([BUILD_CACHE], [test "x$build_cache" = "xyes"])
-
-AC_ARG_ENABLE(docs,
- [AS_HELP_STRING([--disable-docs],[Disable documentation generation])])
-
-AC_PATH_PROG([XML2RFC], [xml2rfc], "no")
-AC_PATH_PROG([XSLTPROC], [xsltproc], "no")
-
-AM_CONDITIONAL([BUILD_SPECIFICATIONS],
- [test "x$enable_docs" != "xno" -a "x$XML2RFC" != "xno" -a "x$XSLTPROC" != "xno"])
-
-
-dnl Let the compiler be a bit more picky. Please note that you cannot
-dnl specify these flags to the compiler before AC_CHECK_FUNCS, because
-dnl the test program will generate a compilation warning and hence fail
-dnl to detect the function ;-)
-if test "$ICC" = "yes"
-then
- dnl ICC trying to be gcc.
- CFLAGS="$CFLAGS -diag-disable 187 -Wall -Werror"
- AC_DEFINE([_GNU_SOURCE],[1],[find sigignore on Linux])
-elif test "$GCC" = "yes"
-then
- GCC_VERSION=`$CC -dumpversion`
- CFLAGS="$CFLAGS -Wall -Werror -pedantic -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls"
- case $GCC_VERSION in
- 4.4.*)
- CFLAGS="$CFLAGS -fno-strict-aliasing"
- ;;
- esac
- AC_DEFINE([_GNU_SOURCE],[1],[find sigignore on Linux])
-elif test "$SUNCC" = "yes"
-then
- CFLAGS="$CFLAGS -errfmt=error -errwarn -errshort=tags"
-fi
-
-AC_CONFIG_FILES(Makefile doc/Makefile)
-AC_OUTPUT
+++ /dev/null
-/* $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.
- */
-
-#include <config.h>
-
-#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 <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#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);
-}
+++ /dev/null
-#! /usr/bin/perl
-#
-use warnings;
-use strict;
-
-use IO::Socket::INET;
-
-use FindBin;
-
-@ARGV == 1 or @ARGV == 2
- or die "Usage: $FindBin::Script HOST:PORT [COUNT]\n";
-
-# Note that it's better to run the test over the wire, because for
-# localhost the task may become CPU bound.
-my $addr = $ARGV[0];
-my $count = $ARGV[1] || 10_000;
-
-my $sock = IO::Socket::INET->new(PeerAddr => $addr,
- Timeout => 3);
-die "$!\n" unless $sock;
-
-
-# By running 'noreply' test first we also ensure there are no reply
-# packets left in the network.
-foreach my $noreply (1, 0) {
- use Time::HiRes qw(gettimeofday tv_interval);
-
- print "'noreply' is ", $noreply ? "enabled" : "disabled", ":\n";
- my $param = $noreply ? 'noreply' : '';
- my $start = [gettimeofday];
- foreach (1 .. $count) {
- print $sock "add foo 0 0 1 $param\r\n1\r\n";
- scalar<$sock> unless $noreply;
- print $sock "set foo 0 0 1 $param\r\n1\r\n";
- scalar<$sock> unless $noreply;
- print $sock "replace foo 0 0 1 $param\r\n1\r\n";
- scalar<$sock> unless $noreply;
- print $sock "append foo 0 0 1 $param\r\n1\r\n";
- scalar<$sock> unless $noreply;
- print $sock "prepend foo 0 0 1 $param\r\n1\r\n";
- scalar<$sock> unless $noreply;
- print $sock "incr foo 1 $param\r\n";
- scalar<$sock> unless $noreply;
- print $sock "decr foo 1 $param\r\n";
- scalar<$sock> unless $noreply;
- print $sock "delete foo $param\r\n";
- scalar<$sock> unless $noreply;
- }
- my $end = [gettimeofday];
- printf("update commands: %.2f secs\n\n", tv_interval($start, $end));
-}
+++ /dev/null
-#!/usr/bin/perl
-use strict;
-use FindBin qw($Bin);
-chdir "$Bin/.." or die;
-
-my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md);
-push(@exempted, glob("doc/*.xml"));
-push(@exempted, glob("doc/xml2rfc/*.xsl"));
-push(@exempted, glob("m4/*backport*m4"));
-my %exempted_hash = map { $_ => 1 } @exempted;
-
-my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`;
-my @files = grep { ! $exempted_hash{$_} } @stuff;
-
-unless (@files) {
- warn "ERROR: You don't seem to be running this from a git checkout\n";
- exit;
-}
-
-foreach my $f (@files) {
- open(my $fh, $f) or die;
- my $before = do { local $/; <$fh>; };
- close ($fh);
- my $after = $before;
- $after =~ s/\t/ /g;
- $after =~ s/ +$//mg;
- $after .= "\n" unless $after =~ /\n$/;
- next if $after eq $before;
- open(my $fh, ">$f") or die;
- print $fh $after;
- close($fh);
-}
+++ /dev/null
-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.
-
-
-Major authors:
---------------
-
-Brad Fitzpatrick <brad@danga.com> -- maintainer, original implementations
-
-Anatoly Vorobey <mellon@pobox.com> -- lots of the modern server code
-
-Steven Grimm <sgrimm@facebook.com> -- iov writing (less CPU), UDP mode,
- non-2.0 slab mantissas, multithread, ...
-
-Other Contributors
-------------------
-
-Evan Martin <evan@danga.com>
-Nathan Neulinger <nneul@umr.edu>
-Eric Hodel <drbrain@segment7.net>
-Michael Johnson <ahze@ahze.net>
-Paul Querna <chip@corelands.com>
-Jamie McCarthy <jamie@mccarthy.vg>
-Philip Neustrom <philipn@gmail.com>
-Andrew O'Brien <andrewo@oriel.com.au>
-Josh Rotenberg <joshrotenberg@gmail.com>
-Robin H. Johnson <robbat2@gentoo.org>
-Tim Yardley <liquid@haveheart.com>
-Paolo Borelli <paolo.borelli@gmail.com>
-Eli Bingham <eli@pandora.com>
-Jean-Francois Bustarret <jfbustarret@wat.tv>
-Paul G <paul-lists@perforge.com>
-Paul Lindner <lindner@inuus.com>
-Dormando <dormando@rydia.net>
-Dustin Sallings <dustin@spy.net>
-Chris Goffinet <goffinet@yahoo-inc.com>
-Tomash Brechko <tomash.brechko@gmail.com>
-Brian Aker <brian@tangent.org>
-Trond Norbye <trond.norbye@sun.com>
+++ /dev/null
-# Doxyfile 1.5.2
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file that
-# follow. The default is UTF-8 which is also the encoding used for all text before
-# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
-# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
-# possible encodings.
-
-DOXYFILE_ENCODING = UTF-8
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME = memcached
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER = 0.8
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = doxygen
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
-# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
-# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
-# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
-
-OUTPUT_LANGUAGE = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF = NO
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF =
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC = YES
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
-
-STRIP_FROM_PATH =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful is your file systems
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like the Qt-style comments (thus requiring an
-# explicit @brief command for a brief description.
-
-JAVADOC_AUTOBRIEF = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the DETAILS_AT_TOP tag is set to YES then Doxygen
-# will output the detailed description near the top, like JavaDoc.
-# If set to NO, the detailed description appears after the member
-# documentation.
-
-DETAILS_AT_TOP = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
-OPTIMIZE_OUTPUT_FOR_C = NO
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for Java.
-# For instance, namespaces will be presented as packages, qualified scopes
-# will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA = NO
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
-# include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also make the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT = NO
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING = YES
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC = NO
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS = NO
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS = YES
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS = NO
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or define consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and defines in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES = YES
-
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES = NO
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from the
-# version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR = YES
-
-# This WARN_NO_PARAMDOC option can be abled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT = ..
-
-# This tag can be used to specify the character encoding of the source files that
-# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
-# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
-# See http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-INPUT_ENCODING = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
-
-FILE_PATTERNS = *.h \
- *.c
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE = NO
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE = config.h
-
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
-# directories that are symbolic links (a Unix filesystem feature) are excluded
-# from the input.
-
-EXCLUDE_SYMLINKS = NO
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
-EXCLUDE_PATTERNS =
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the output.
-# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
-# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS =
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output. If FILTER_PATTERNS is specified, this tag will be
-# ignored.
-
-INPUT_FILTER =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis. Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match. The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
-# is applied to all files.
-
-FILTER_PATTERNS =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
-SOURCE_BROWSER = NO
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES (the default)
-# then for each documented function all documented
-# functions referencing it will be listed.
-
-REFERENCED_BY_RELATION = YES
-
-# If the REFERENCES_RELATION tag is set to YES (the default)
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
-REFERENCES_RELATION = YES
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code. Otherwise they will link to the documentstion.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT = html
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
-
-HTML_STYLESHEET =
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND = NO
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX = NO
-
-# This tag can be used to set the number of enum values (range [1..20])
-# that doxygen will group on one line in the generated HTML documentation.
-
-ENUM_VALUES_PER_LINE = 4
-
-# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
-# generated containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
-# probably better off using the HTML help feature.
-
-GENERATE_TREEVIEW = NO
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH = 250
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-
-LATEX_CMD_NAME = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS = NO
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX = NO
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS = NO
-
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN = YES
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING = YES
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader. This is useful
-# if you want to understand what is going on. On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH =
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED =
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition.
-
-EXPAND_AS_DEFINED =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all function-like macros that are alone
-# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse
-# the parser if not removed.
-
-SKIP_FUNCTION_MACROS = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option is superseded by the HAVE_DOT option below. This is only a
-# fallback. It is recommended to install and use dot, since it yields more
-# powerful graphs.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
-# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
-# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
-# be found in the default search path.
-
-MSCGEN_PATH =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT = NO
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK = NO
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH = YES
-
-# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
-# generate a call dependency graph for every global function or class method.
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable call graphs for selected
-# functions only using the \callgraph command.
-
-CALL_GRAPH = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
-# generate a caller dependency graph for every global function or class method.
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable caller graphs for selected
-# functions only using the \callergraph command.
-
-CALLER_GRAPH = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY = YES
-
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
-# If left blank png will be used.
-
-DOT_IMAGE_FORMAT = png
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS =
-
-# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen will always
-# show the root nodes and its direct children regardless of this setting.
-
-DOT_GRAPH_MAX_NODES = 50
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, which results in a white background.
-# Warning: Depending on the platform used, enabling this option may lead to
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
-# read).
-
-DOT_TRANSPARENT = NO
-
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
-
-DOT_MULTI_TARGETS = NO
-
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE = NO
+++ /dev/null
-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
-
-%.txt: %.full
- @XML2RFC@ $< $@
-
-%.chk: %.xml xml2rfc/rfc2629-refchk.xsl
- @XSLTPROC@ xml2rfc/rfc2629-refchk.xsl $< >$@
-
-%.full: %.xml xml2rfc/rfc2629-noinc.xsl
- @XSLTPROC@ xml2rfc/rfc2629-noinc.xsl $< >$@
-
+++ /dev/null
-.TH MEMCACHED 1 "April 11, 2005"
-.SH NAME
-memcached \- high-performance memory object caching system
-.SH SYNOPSIS
-.B memcached
-.RI [ options ]
-.br
-.SH DESCRIPTION
-This manual page documents briefly the
-.B memcached
-memory object caching daemon.
-.PP
-.B memcached
-is a flexible memory object caching daemon designed to alleviate database load
-in dynamic web applications by storing objects in memory. It's based on
-libevent to scale to any size needed, and is specifically optimized to avoid
-swapping and always use non-blocking I/O.
-.br
-.SH OPTIONS
-These programs follow the usual GNU command line syntax. A summary of options
-is included below.
-.TP
-.B \-s <file>
-Unix socket path to listen on (disables network support).
-.TP
-.B \-a <perms>
-Permissions (in octal format) for Unix socket created with \-s option.
-.TP
-.B \-l <ip_addr>
-Listen on <ip_addr>; default to INADDR_ANY. This is an important option to
-consider as there is no other way to secure the installation. Binding to an
-internal or firewalled network interface is suggested.
-.TP
-.B \-d
-Run memcached as a daemon.
-.TP
-.B \-u <username>
-Assume the identity of <username> (only when run as root).
-.TP
-.B \-m <num>
-Use <num> MB memory max to use for object storage; the default is 64 megabytes.
-.TP
-.B \-c <num>
-Use <num> max simultaneous connections; the default is 1024.
-.TP
-.B \-R <num>
-This option seeks to prevent client starvation by setting a limit to the
-number of sequential requests the server will process from an individual
-client connection. Once a connection has exceeded this value, the server will
-attempt to process I/O on other connections before handling any further
-request from this connection. The default value for this option is 20.
-.TP
-.B \-k
-Lock down all paged memory. This is a somewhat dangerous option with large
-caches, so consult the README and memcached homepage for configuration
-suggestions.
-.TP
-.B \-p <num>
-Listen on TCP port <num>, the default is port 11211.
-.TP
-.B \-U <num>
-Listen on UDP port <num>, the default is port 11211, 0 is off.
-.TP
-.B \-M
-Disable automatic removal of items from the cache when out of memory.
-Additions will not be possible until adequate space is freed up.
-.TP
-.B \-r
-Raise the core file size limit to the maximum allowable.
-.TP
-.B \-f <factor>
-Use <factor> as the multiplier for computing the sizes of memory chunks that
-items are stored in. A lower value may result in less wasted memory depending
-on the total amount of memory available and the distribution of item sizes.
-The default is 1.25.
-.TP
-.B \-n <size>
-Allocate a minimum of <size> bytes for the item key, value, and flags. The
-default is 48. If you have a lot of small keys and values, you can get a
-significant memory efficiency gain with a lower value. If you use a high
-chunk growth factor (\-f option), on the other hand, you may want to increase
-the size to allow a bigger percentage of your items to fit in the most densely
-packed (smallest) chunks.
-.TP
-.B \-C
-Disable the use of CAS (and reduce the per-item size by 8 bytes).
-.TP
-.B \-h
-Show the version of memcached and a summary of options.
-.TP
-.B \-v
-Be verbose during the event loop; print out errors and warnings.
-.TP
-.B \-vv
-Be even more verbose; same as \-v but also print client commands and
-responses.
-.TP
-.B \-i
-Print memcached and libevent licenses.
-.TP
-.B \-P <filename>
-Print pidfile to <filename>, only used under \-d option.
-.TP
-.B \-t <threads>
-Number of threads to use to process incoming requests. This option is only
-meaningful if memcached was compiled with thread support enabled. It is
-typically not useful to set this higher than the number of CPU cores on the
-memcached server. The default is 4.
-.TP
-.B \-D <char>
-Use <char> as the delimiter between key prefixes and IDs. This is used for
-per-prefix stats reporting. The default is ":" (colon). If this option is
-specified, stats collection is turned on automatically; if not, then it may
-be turned on by sending the "stats detail on" command to the server.
-.TP
-.B \-L
-Try to use large memory pages (if available). Increasing the memory page size
-could reduce the number of TLB misses and improve the performance. In order to
-get large pages from the OS, memcached will allocate the total item-cache in
-one large chunk. Only available if supported on your OS.
-.TP
-.B \-B <proto>
-Specify the binding protocol to use. By default, the server will
-autonegotiate client connections. By using this option, you can
-specify the protocol clients must speak. Possible options are "auto"
-(the default, autonegotiation behavior), "ascii" and "binary".
-.TP
-.B \-I <size>
-Override the default size of each slab page. Default is 1mb. Default is 1m,
-minimum is 1k, max is 128m. Adjusting this value changes the item size limit.
-Beware that this also increases the number of slabs (use -v to view), and the
-overal memory usage of memcached.
-.TP
-.B \-o <options>
-Comma separated list of extended or experimental options. See -h or wiki for
-up to date list.
-.br
-.SH LICENSE
-The memcached daemon is copyright Danga Interactive and is distributed under
-the BSD license. Note that daemon clients are licensed separately.
-.br
-.SH SEE ALSO
-The README file that comes with memcached
-.br
-.B http://www.danga.com/memcached
-.SH AUTHOR
-The memcached daemon was written by Anatoly Vorobey
-.B <mellon@pobox.com>
-and Brad Fitzpatrick
-.B <brad@danga.com>
-and the rest of the crew of Danga Interactive
-.B http://www.danga.com
-.br
+++ /dev/null
-<?xml version="1.0" encoding="US-ASCII"?>
-<!DOCTYPE rfc SYSTEM "xml2rfc/rfc2629.dtd">
-<?xml-stylesheet type='text/xsl' href='xml2rfc/rfc2629.xslt'?>
-<?rfc toc="yes"?>
-<?rfc strict="yes"?>
-<?rfc symrefs="yes"?>
-<?rfc sortrefs="yes" ?>
-<?rfc compact="yes" ?>
-<?rfc subcompact="yes" ?>
-<rfc category="info" docName="draft-stone-memcache-udp-01" ipr="none">
-
- <front>
-
- <title abbrev="Memcache Over UDP"> Memcache Binary Protocol: Extensions for UDP </title>
-
- <author fullname="Aaron Stone" surname="Aaron Stone" role="editor">
- <organization>Six Apart, Ltd.</organization>
- <address>
- <postal>
- <street>548 4th Street</street>
- <city>San Francisco</city>
- <region>CA</region>
- <code>94107</code>
- <country>USA</country>
- </postal>
- <email>aaron@serendipity.palo-alto.ca.us</email>
- </address>
- </author>
-
- <date day="14" month="December" year="2007" />
-
- <area>Applications</area>
-
- <keyword>memcache memcached cache udp</keyword>
-
- <abstract>
- <t>
- This memo explains extensions to the memcache binary protocol for use in a UDP environment.
- </t>
-
- <t>
- Memcache is a high performance key-value cache. It is intentionally a
- dumb cache, optimized for speed only. Applications using memcache do
- not rely on it for data -- a persistent database with guaranteed reliability
- is strongly recommended -- but applications can run much faster when
- cached data is available in memcache.
- </t>
- </abstract>
- </front>
-
- <middle>
- <section anchor="introduction" title="Introduction">
- <t>
- Memcache is a high performance key-value cache. It is intentionally a
- dumb cache, optimized for speed only. Applications using memcache do
- not rely on it for data -- a persistent database with guaranteed reliability
- is strongly recommended -- but applications can run much faster when
- cached data is available in memcache.
- </t>
- <t>
- Sites may find that, due to their network architecture or application usage patterns,
- the stateless <xref target="UDP"/> protocol better suits their needs. This document
- provides extensions and descriptions of use of the <xref target="MEMCACHE">memcache protocol</xref>
- in a UDP environment.
- </t>
- <t>
- It is a goal of this document to provide sufficient information in each UDP packet
- as to avoid any requirement for statefulness on the part of the server nor significant
- caching of outstanding packets on the part of the client.
- </t>
- <section anchor="conventions" title="Conventions Used In This Document">
- <t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in <xref target="KEYWORDS"/>.
- </t>
- </section>
- </section>
-
- <section anchor="values" title="Defined Values">
- <section anchor="value-magic" title="Magic Byte">
- <t>
- The magic bytes remains the same as in <xref target="MEMCACHE"/>.
- </t>
- </section>
-
- <section anchor="value-status" title="Response Status">
- <t>
- Additional status values:
- <list hangIndent="8" style="hanging">
- <t hangText="0x0004">Value is larger than a single response packet</t>
- </list>
- </t>
- </section>
-
- <section anchor="value-opcodes" title="Command Opcodes">
- <t>
- Additional opcode values:
- <list hangIndent="8" style="hanging">
- <t hangText="0x0C">Get Range</t>
- <t hangText="0x0D">Set Range</t>
- </list>
- </t>
- </section>
-
- <section anchor="value-types" title="Data Types">
- <t>
- There are no new data types in this extension.
- </t>
- </section>
- </section>
-
- <section anchor="commands" title="Commands">
-
- <section anchor="command-get" title="Get Response">
- <t>
- This section extends the behavior of the Get and GetQ commands as described in
- <xref target="MEMCACHE" x:sec="command-get"/>.
- </t>
-
- <t>
- When a Get or GetQ request is made via UDP, and the value of the key for which
- the request was made is larger than can be placed into a single UDP packet (noting
- that the protocol header must also be counted), a Get Range response packet
- MUST be sent instead of the Get response packet. In this instance:
- <list style="numbers">
- <t>The Status field of the response header MUST be 0x0004.</t>
- <t>The Offset field of the GetR response extras MUST be 0.</t>
- <t>The Length field of the GetR response extras, and the data contained in
- the Value field of the packet, SHOULD be the maximum
- allowed length of a UDP packet, less the space required by the header
- and extras; however it MAY be any amount below this maximum.</t>
- <t>The Total value length field of the response extras MUST be the
- actual length of the complete value.</t>
- </list>
- </t>
-
- <t>
- The client, upon receipt of a Get Range response bearing Status 0x004
- and a Message ID corresponding to its Get request, shall then know that
- it has received only the first portion of the value. The client MAY choose
- to request the remaining portion of the value by sending one or more Get Range requests.
- </t>
- </section>
-
- <section anchor="command-getr-request" title="Get Range Request">
- <t>
- The Get Range request is primarily intended for use over a UDP transport
- to request byte ranges of the value for a key. In the event that the Data version
- check fails to match that of the key, an error MUST be returned.
- </t>
- <t>
- <figure>
- <preamble>Extra data for get range request:</preamble>
- <artwork>
-Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Flags |
- +---------------+---------------+---------------+---------------+
- 4| Data version check |
- | |
- +---------------+---------------+---------------+---------------+
-12| Offset |
- +---------------+---------------+---------------+---------------+
-16| Length |
- +---------------+---------------+---------------+---------------+
-Total 20 bytes
- </artwork></figure>
- </t>
- </section>
-
- <section anchor="command-getr-response" title="Get Range Response">
- <t>
- The Get Range request is primarily intended for use over a UDP transport
- to indicate the location of the bytes of the value for a key contained in
- a given packet. A client receives enough information in each Get Range
- extras to construct an appropriately sized buffer in its own memory and
- blindly insert the contents of the packet at the given byte offset.
- </t>
- <t>
- <figure>
- <preamble>Extra data for get range response:</preamble>
- <artwork>
-Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Flags |
- +---------------+---------------+---------------+---------------+
- 4| Data version check |
- | |
- +---------------+---------------+---------------+---------------+
-12| Offset |
- +---------------+---------------+---------------+---------------+
-16| Length |
- +---------------+---------------+---------------+---------------+
-20| Total value length |
- +---------------+---------------+---------------+---------------+
-Total 24 bytes
- </artwork></figure>
- </t>
- </section>
-
- </section>
-
- <section anchor="security" title="Security Considerations">
- <t>
- This document does not introduce any new security considerations
- beyond those discussed in <xref target="MEMCACHE" x:sec="security"/>.
- </t>
- </section>
-
- </middle>
-
- <back>
- <references title="Normative References">
- <dwdrfc-ref anchor='UDP' src='http://xml.resource.org/public/rfc/bibxml/reference.RFC.0768.xml'/>
- <dwdrfc-ref anchor='KEYWORDS' src='http://xml.resource.org/public/rfc/bibxml/reference.RFC.2119.xml'/>
- <!-- FIXME: Get a draft reference for the base document. -->
- <dwdrfc-ref anchor='MEMCACHE' src='http://xml.resource.org/public/rfc/bibxml/reference.RFC.2119.xml'/>
- </references>
- </back>
-
-</rfc>
-
+++ /dev/null
-<?xml version="1.0" encoding="US-ASCII"?>
-<!DOCTYPE rfc SYSTEM "xml2rfc/rfc2629.dtd">
-<?xml-stylesheet type='text/xsl' href='xml2rfc/rfc2629.xslt'?>
-<?rfc toc="yes"?>
-<?rfc strict="yes"?>
-<?rfc symrefs="yes"?>
-<?rfc sortrefs="yes" ?>
-<?rfc compact="yes" ?>
-<?rfc subcompact="yes" ?>
-<rfc category="info" docName="draft-stone-memcache-binary-01" ipr="none">
- <front>
- <title> Memcache Binary Protocol </title>
-
- <author fullname="Aaron Stone" surname="Stone" role="editor">
- <organization>Six Apart, Ltd.</organization>
- <address>
- <postal>
- <street>548 4th Street</street>
- <city>San Francisco</city>
- <region>CA</region>
- <code>94107</code>
- <country>USA</country>
- </postal>
- <email>aaron@serendipity.palo-alto.ca.us</email>
- </address>
- </author>
- <author fullname="Trond Norbye" surname="Norbye" role="editor">
- <organization>Sun Microsystems, INC</organization>
- <address>
- <postal>
- <street>Haakon VII g. 7B</street>
- <city>Trondheim</city>
- <code>NO-7485 Trondheim</code>
- <country>Norway</country>
- </postal>
- <email>trond.norbye@sun.com</email>
- </address>
- </author>
- <date day="28" month="August" year="2008" />
- <area>Applications</area>
- <keyword>memcache memcached cache</keyword>
- <abstract>
- <t>
- This memo explains the memcache binary protocol for informational
- purposes.
- </t>
- <t>
- Memcache is a high performance key-value cache. It is intentionally a
- dumb cache, optimized for speed only. Applications using memcache do
- not rely on it for data -- a persistent database with guaranteed
- reliability is strongly recommended -- but applications can run much
- faster when cached data is available in memcache.
- </t>
- </abstract>
- </front>
-
- <middle>
- <section anchor="introduction" title="Introduction">
- <t>
- Memcache is a high performance key-value cache. It is intentionally a
- dumb cache, optimized for speed only. Applications using memcache should
- not rely on it for data -- a persistent database with guaranteed
- reliability is strongly recommended -- but applications can run much
- faster when cached data is available in memcache.
- </t>
- <t>
- Memcache was originally written to make
- <xref target="LJ">LiveJournal</xref> faster. It now powers all of
- the fastest web sites that you love.
- </t>
- <section anchor="conventions" title="Conventions Used In This Document">
- <t>
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in
- <xref target="KEYWORDS"/>.
- </t>
- </section>
- </section>
-
- <section anchor="packet" title="Packet Structure">
- <figure>
- <preamble>General format of a packet:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0/ HEADER /
- / /
- / /
- / /
- +---------------+---------------+---------------+---------------+
- 24/ COMMAND-SPECIFIC EXTRAS (as needed) /
- +/ (note length in the extras length header field) /
- +---------------+---------------+---------------+---------------+
- m/ Key (as needed) /
- +/ (note length in key length header field) /
- +---------------+---------------+---------------+---------------+
- n/ Value (as needed) /
- +/ (note length is total body length header field, minus /
- +/ sum of the extras and key length body fields) /
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
- </artwork>
- </figure>
-
- <figure>
- <preamble>Request header:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Magic | Opcode | Key length |
- +---------------+---------------+---------------+---------------+
- 4| Extras length | Data type | Reserved |
- +---------------+---------------+---------------+---------------+
- 8| Total body length |
- +---------------+---------------+---------------+---------------+
- 12| Opaque |
- +---------------+---------------+---------------+---------------+
- 16| CAS |
- | |
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
- </artwork>
- </figure>
-
- <figure>
- <preamble>Response header:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Magic | Opcode | Key Length |
- +---------------+---------------+---------------+---------------+
- 4| Extras length | Data type | Status |
- +---------------+---------------+---------------+---------------+
- 8| Total body length |
- +---------------+---------------+---------------+---------------+
- 12| Opaque |
- +---------------+---------------+---------------+---------------+
- 16| CAS |
- | |
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
- </artwork>
- </figure>
-
- <t>
- Header fields:
- <list hangIndent="20" style="hanging">
- <t hangText="Magic">Magic number.</t>
- <t hangText="Opcode">Command code.</t>
- <t hangText="Key length">Length in bytes of the text key that follows the command extras.</t>
- <t hangText="Status">Status of the response (non-zero on error).</t>
- <t hangText="Extras length">Length in bytes of the command extras.</t>
- <t hangText="Data type">Reserved for future use (Sean is using this soon).</t>
- <t hangText="Reserved">Really reserved for future use (up for grabs).</t>
- <t hangText="Total body length">Length in bytes of extra + key + value.</t>
- <t hangText="Opaque">Will be copied back to you in the response.</t>
- <t hangText="CAS">Data version check.</t>
- </list>
- </t>
- </section>
-
- <section anchor="values" title="Defined Values">
- <section anchor="value-magic" title="Magic Byte">
- <t>
- <list hangIndent="8" style="hanging">
- <t hangText="0x80">Request packet for this protocol version</t>
- <t hangText="0x81">Response packet for this protocol version</t>
- </list>
- </t>
-
- <t>
- Magic byte / version. For each version of the protocol, we'll use a
- different request/response value pair. This is useful for protocol
- analyzers to distinguish the nature of the packet from the direction
- which it is moving. Note, it is common to run a memcached instance on
- a host that also runs an application server. Such a host will both
- send and receive memcache packets.
- </t>
-
- <t>
- The version should hopefully correspond only to different meanings of
- the command byte. In an ideal world, we will not change the header
- format. As reserved bytes are given defined meaning, the protocol
- version / magic byte values should be incremented.
- </t>
-
- <t>
- Traffic analysis tools are encouraged to identify memcache packets
- and provide detailed interpretation if the magic bytes are recognized
- and otherwise to provide a generic breakdown of the packet. Note, that
- the key and value positions can always be identified even if the magic
- byte or command opcode are not recognized.
- </t>
- </section>
-
- <section anchor="value-status" title="Response Status">
- <t>
- Possible values of this two-byte field:
- <list hangIndent="8" style="hanging">
- <t hangText="0x0000">No error</t>
- <t hangText="0x0001">Key not found</t>
- <t hangText="0x0002">Key exists</t>
- <t hangText="0x0003">Value too large</t>
- <t hangText="0x0004">Invalid arguments</t>
- <t hangText="0x0005">Item not stored</t>
- <t hangText="0x0006">Incr/Decr on non-numeric value.</t>
- <t hangText="0x0081">Unknown command</t>
- <t hangText="0x0082">Out of memory</t>
- </list>
- </t>
- </section>
-
- <section anchor="value-opcodes" title="Command Opcodes">
- <t>
- Possible values of the one-byte field:
- <list hangIndent="8" style="hanging">
- <t hangText="0x00">Get</t>
- <t hangText="0x01">Set</t>
- <t hangText="0x02">Add</t>
- <t hangText="0x03">Replace</t>
- <t hangText="0x04">Delete</t>
- <t hangText="0x05">Increment</t>
- <t hangText="0x06">Decrement</t>
- <t hangText="0x07">Quit</t>
- <t hangText="0x08">Flush</t>
- <t hangText="0x09">GetQ</t>
- <t hangText="0x0A">No-op</t>
- <t hangText="0x0B">Version</t>
- <t hangText="0x0C">GetK</t>
- <t hangText="0x0D">GetKQ</t>
- <t hangText="0x0E">Append</t>
- <t hangText="0x0F">Prepend</t>
- <t hangText="0x10">Stat</t>
- <t hangText="0x11">SetQ</t>
- <t hangText="0x12">AddQ</t>
- <t hangText="0x13">ReplaceQ</t>
- <t hangText="0x14">DeleteQ</t>
- <t hangText="0x15">IncrementQ</t>
- <t hangText="0x16">DecrementQ</t>
- <t hangText="0x17">QuitQ</t>
- <t hangText="0x18">FlushQ</t>
- <t hangText="0x19">AppendQ</t>
- <t hangText="0x1A">PrependQ</t>
- </list>
- </t>
- <t>
- As a convention all of the commands ending with "Q" for
- Quiet. A quiet version of a command will omit responses
- that are considered uninteresting. Whether a given response
- is interesting is dependent upon the command. See the
- descriptions of the
- <xref target="command-get">set commands</xref>
- and <xref target="command-set">set commands</xref> for
- examples of commands that include quiet variants.
- </t>
- </section>
-
- <section anchor="value-types" title="Data Types">
- <t>
- Possible values of the one-byte field:
- <list hangIndent="8" style="hanging">
- <t hangText="0x00">Raw bytes</t>
- </list>
- </t>
- </section>
- </section>
-
- <section title="Commands">
- <section anchor="command-introduction" title="Introduction">
- <t>
- All communication is initiated by a request from the client,
- and the server will respond to each request with zero or
- multiple packets for each request. If the status code of a response
- packet is non-nil, the body of the packet will contain a textual error
- message. If the status code is nil, the command opcode will define the
- layout of the body of the message.
- </t>
- <section anchor="command-introduction-example" title="Example">
- <t>
- The following figure illustrates the packet layout for
- a packet with an error message.
- </t>
- <figure>
- <preamble>Packet layout:</preamble>
- <artwork>
-
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x01 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x09 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x4e ('N') | 0x6f ('o') | 0x74 ('t') | 0x20 (' ') |
- +---------------+---------------+---------------+---------------+
- 28| 0x66 ('f') | 0x6f ('o') | 0x75 ('u') | 0x6e ('n') |
- +---------------+---------------+---------------+---------------+
- 32| 0x64 ('d') |
- +---------------+
- Total 33 bytes (24 byte header, and 9 bytes value)
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x00
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Status (6,7) : 0x0001
-Total body (8-11) : 0x00000009
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key : None
-Value (24-32): The textual string "Not found"
- </artwork>
- </figure>
- </section>
- </section>
-
- <section anchor="command-get" title="Get, Get Quietly, Get Key, Get Key Quietly">
- <t>
- Request:
- </t>
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MUST have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- Response (if found):
- </t>
- <t>
- <list style="empty">
- <t>MUST have extras.</t>
- <t>MAY have key.</t>
- <t>MAY have value.</t>
- </list>
- </t>
-
- <t>
- <list style="symbols">
- <t>4 byte flags</t>
- </list>
- </t>
-
- <t>
- <figure>
- <preamble>Extra data for the get commands:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Flags |
- +---------------+---------------+---------------+---------------+
-
- Total 4 bytes
- </artwork>
- </figure>
- </t>
-
- <t>
- The get command gets a single key. The getq command is both mum
- on cache miss and quiet, holding its response until a non-quiet
- command is issued. Getk and getkq differs from get and getq by
- adding the key into the response packet.
- </t>
-
- <t>
- You're not guaranteed a response to a getq/getkq cache hit until
- you send a non-getq/getkq command later, which uncorks the
- server and bundles up IOs to send to the client in one go.
- </t>
-
- <t>
- Clients should implement multi-get (still important for
- reducing network roundtrips!) as n pipelined requests, the
- first n-1 being getq/getkq, the last being a regular
- get/getk. That way you're guaranteed to get a response, and
- you know when the server's done. You can also do the naive
- thing and send n pipelined get/getks, but then you could potentially
- get back a lot of "NOT_FOUND" error code packets.
- Alternatively, you can send 'n' getq/getkqs, followed by a
- 'noop' command.
- </t>
-
- <section anchor="command-get-example" title="Example">
- <t>
- To request the data associated with the key "Hello" the
- following fields must be specified in the packet.
- </t>
- <figure>
- <preamble>get request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x00 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
- +---------------+---------------+---------------+---------------+
- 28| 0x6f ('o') |
- +---------------+
-
- Total 29 bytes (24 byte header, and 5 bytes key)
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x00
-Key length (2,3) : 0x0005
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000005
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key (24-29): The textual string: "Hello"
-Value : None
- </artwork>
- </figure>
- <t>If the item exist on the server the following packet is returned,
- otherwise a packet with status code != 0 will be returned (see
- <xref target="command-introduction">Introduction</xref>)
- </t>
- <figure>
- <preamble>get/getq response:</preamble>
- <artwork>
-
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x04 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x09 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x01 |
- +---------------+---------------+---------------+---------------+
- 24| 0xde | 0xad | 0xbe | 0xef |
- +---------------+---------------+---------------+---------------+
- 28| 0x57 ('W') | 0x6f ('o') | 0x72 ('r') | 0x6c ('l') |
- +---------------+---------------+---------------+---------------+
- 32| 0x64 ('d') |
- +---------------+
-
- Total 33 bytes (24 byte header, 4 byte extras and 5 byte value)
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x00
-Key length (2,3) : 0x0000
-Extra length (4) : 0x04
-Data type (5) : 0x00
-Status (6,7) : 0x0000
-Total body (8-11) : 0x00000009
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000001
-Extras :
- Flags (24-27): 0xdeadbeef
-Key : None
-Value (28-32): The textual string "World"
- </artwork>
- </figure>
- <figure>
- <preamble>getk/getkq response:</preamble>
- <artwork>
-
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x00 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 4| 0x04 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x09 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x01 |
- +---------------+---------------+---------------+---------------+
- 24| 0xde | 0xad | 0xbe | 0xef |
- +---------------+---------------+---------------+---------------+
- 28| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
- +---------------+---------------+---------------+---------------+
- 32| 0x6f ('o') | 0x57 ('W') | 0x6f ('o') | 0x72 ('r') |
- +---------------+---------------+---------------+---------------+
- 36| 0x6c ('l') | 0x64 ('d') |
- +---------------+---------------+
-
- Total 38 bytes (24 byte header, 4 byte extras, 5 byte key
- and 5 byte value)
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x00
-Key length (2,3) : 0x0005
-Extra length (4) : 0x04
-Data type (5) : 0x00
-Status (6,7) : 0x0000
-Total body (8-11) : 0x00000009
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000001
-Extras :
- Flags (24-27): 0xdeadbeef
-Key (28-32): The textual string: "Hello"
-Value (33-37): The textual string: "World"
- </artwork>
- </figure>
- </section>
- </section>
-
- <section anchor="command-set" title="Set, Add, Replace">
- <t>
- <list style="empty">
- <t>MUST have extras.</t>
- <t>MUST have key.</t>
- <t>MUST have value.</t>
- </list>
- </t>
-
- <t>
- <list style="symbols">
- <t>4 byte flags</t>
- <t>4 byte expiration time</t>
- </list>
- </t>
-
- <figure>
- <preamble>Extra data for set/add/replace:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Flags |
- +---------------+---------------+---------------+---------------+
- 4| Expiration |
- +---------------+---------------+---------------+---------------+
- Total 8 bytes
- </artwork>
- </figure>
-
- <t>
- If the Data Version Check (CAS) is nonzero, the requested
- operation MUST only succeed if the item exists and has a CAS value
- identical to the provided value.
- </t>
-
- <t>
- Add MUST fail if the item already exist.
- </t>
-
- <t>
- Replace MUST fail if the item doesn't exist.
- </t>
-
- <t>
- Set should store the data unconditionally if the item exists
- or not.
- </t>
-
- <t>
- Quiet mutations only return responses on failure. Success
- is considered the general case and is suppressed when in
- quiet mode, but errors should not be allowed to go
- unnoticed.
- </t>
-
- <section anchor="command-set-example" title="Example">
- <t>The following figure shows an add-command for
- <list style="empty">
- <t>Key: "Hello"</t>
- <t>Value: "World"</t>
- <t>Flags: 0xdeadbeef</t>
- <t>Expiry: in two hours</t>
- </list>
- </t>
- <figure>
- <preamble>Add request:</preamble>
- <artwork>
-
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x02 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 4| 0x08 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x12 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0xde | 0xad | 0xbe | 0xef |
- +---------------+---------------+---------------+---------------+
- 28| 0x00 | 0x00 | 0x0e | 0x10 |
- +---------------+---------------+---------------+---------------+
- 32| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
- +---------------+---------------+---------------+---------------+
- 36| 0x6f ('o') | 0x57 ('W') | 0x6f ('o') | 0x72 ('r') |
- +---------------+---------------+---------------+---------------+
- 40| 0x6c ('l') | 0x64 ('d') |
- +---------------+---------------+
-
- Total 42 bytes (24 byte header, 8 byte extras, 5 byte key and
- 5 byte value)
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x02
-Key length (2,3) : 0x0005
-Extra length (4) : 0x08
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000012
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras :
- Flags (24-27): 0xdeadbeef
- Expiry (28-31): 0x00000e10
-Key (32-36): The textual string "Hello"
-Value (37-41): The textual string "World"
- </artwork>
- </figure>
- <t>
- The response-packet contains no extra data, and the result of the
- operation is signaled through the status code. If the command
- succeeds, the CAS value for the item is returned in the CAS-field
- of the packet.
- </t>
- <figure>
- <preamble>Successful add response:</preamble>
- <artwork>
-
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x02 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x01 |
- +---------------+---------------+---------------+---------------+
-
- Total 24 bytes
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x02
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Status (6,7) : 0x0000
-Total body (8-11) : 0x00000000
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000001
-Extras : None
-Key : None
-Value : None
- </artwork>
- </figure>
- </section>
- </section>
-
- <section anchor="command-delete" title="Delete">
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MUST have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- Delete the item with the specific key.
- </t>
-
- <section anchor="command-delete-example" title="Example">
- <t>The following figure shows a delete message for the
- item "Hello".</t>
- <figure>
- <preamble>Delete request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x04 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
- +---------------+---------------+---------------+---------------+
- 28| 0x6f ('o') |
- +---------------+
-
- Total 29 bytes (24 byte header, 5 byte value)
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x04
-Key length (2,3) : 0x0005
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000005
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key : The textual string "Hello"
-Value : None
- </artwork>
- </figure>
- <t>
- The response-packet contains no extra data, and the result of the
- operation is signaled through the status code.
- </t>
- </section>
- </section>
-
- <section anchor="command-incr" title="Increment, Decrement">
- <t>
- <list style="empty">
- <t>MUST have extras.</t>
- <t>MUST have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- <list style="symbols">
- <t>8 byte value to add / subtract</t>
- <t>8 byte initial value (unsigned)</t>
- <t>4 byte expiration time</t>
- </list>
- </t>
- <figure>
- <preamble>Extra data for incr/decr:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Amount to add |
- | |
- +---------------+---------------+---------------+---------------+
- 8| Initial value |
- | |
- +---------------+---------------+---------------+---------------+
- 16| Expiration |
- +---------------+---------------+---------------+---------------+
- Total 20 bytes
- </artwork>
- </figure>
-
- <t>
- These commands will either add or remove the specified
- amount to the requested counter.
- </t>
- <t>
- If the counter does not exist, one of two things may happen:
- </t>
- <t>
- <list style="numbers">
- <t>If the expiration value is all one-bits (0xffffffff), the
- operation will fail with NOT_FOUND.</t>
- <t>For all other expiration values, the operation will succeed
- by seeding the value for this key with the provided initial
- value to expire with the provided expiration time. The flags
- will be set to zero.</t>
- </list>
- </t>
- <figure>
- <preamble>incr/decr response body:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 64-bit unsigned response. |
- | |
- +---------------+---------------+---------------+---------------+
- Total 8 bytes
- </artwork>
- </figure>
- <section anchor="command-incr-example" title="Example">
- <t>The following figure shows an incr-command for
- <list style="empty">
- <t>Key: "counter"</t>
- <t>Delta: 0x01</t>
- <t>Initial: 0x00</t>
- <t>Expiry: in two hours</t>
- </list>
- </t>
- <figure>
- <preamble>Increment request:</preamble>
- <artwork>
-
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x05 | 0x00 | 0x07 |
- +---------------+---------------+---------------+---------------+
- 4| 0x14 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x1b |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 28| 0x00 | 0x00 | 0x00 | 0x01 |
- +---------------+---------------+---------------+---------------+
- 32| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 36| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 40| 0x00 | 0x00 | 0x0e | 0x10 |
- +---------------+---------------+---------------+---------------+
- 44| 0x63 ('c') | 0x6f ('o') | 0x75 ('u') | 0x6e ('n') |
- +---------------+---------------+---------------+---------------+
- 48| 0x74 ('t') | 0x65 ('e') | 0x72 ('r') |
- +---------------+---------------+---------------+
- Total 51 bytes (24 byte header, 20 byte extras, 7 byte key)
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x05
-Key length (2,3) : 0x0007
-Extra length (4) : 0x14
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x0000001b
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras :
- delta (24-31): 0x0000000000000001
- initial (32-39): 0x0000000000000000
- exipration (40-43): 0x00000e10
-Key : Textual string "counter"
-Value : None
- </artwork>
- </figure>
- <t>
- If the key doesn't exist, the server will respond with the
- initial value. If not the incremented value will be returned.
- Let's assume that the key didn't exist, so the initial value
- is returned.
- </t>
- <figure>
- <preamble>Increment response:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x05 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x08 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 24| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 28| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- Total 32 bytes (24 byte header, 8 byte value)
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x05
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Status (6,7) : 0x0000
-Total body (8-11) : 0x00000008
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000005
-Extras : None
-Key : None
-Value : 0x0000000000000000
- </artwork>
- </figure>
- </section>
- </section>
-
- <section anchor="command-quit" title="quit">
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MUST NOT have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- Close the connection to the server.
- </t>
-
- <section anchor="command-quit-example" title="Example">
- <figure>
- <preamble>Quit request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x07 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x07
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000000
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key : None
-Value : None
- </artwork>
- </figure>
- <t>
- The response-packet contains no extra data, and the result of the
- operation is signaled through the status code. The server will
- then close the connection.
- </t>
- </section>
- </section>
-
- <section anchor="command-flush" title="Flush">
- <t>
- <list style="empty">
- <t>MAY have extras.</t>
- <t>MUST NOT have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- <list style="symbols">
- <t>4 byte expiration time</t>
- </list>
- </t>
- <figure>
- <preamble>Extra data for flush:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| Expiration |
- +---------------+---------------+---------------+---------------+
- Total 4 bytes
- </artwork>
- </figure>
- <t>
- Flush the items in the cache now or some time in the future as
- specified by the expiration field. See the documentation of the
- textual protocol for the full description on how to specify the
- expiration time.
- </t>
- <section anchor="command-flush-example" title="Example">
- <t>
- To flush the cache (delete all items) in two hours, the set
- the following values in the request
- </t>
- <figure>
- <preamble>Flush request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x08 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x04 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x04 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x00 | 0x00 | 0x0e | 0x10 |
- +---------------+---------------+---------------+---------------+
- Total 28 bytes (24 byte header, 4 byte body)
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x08
-Key length (2,3) : 0x0000
-Extra length (4) : 0x04
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000004
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras :
- Expiry (24-27): 0x000e10
-Key : None
-Value : None
- </artwork>
- </figure>
- <t>
- The response-packet contains no extra data, and the result of the
- operation is signaled through the status code.
- </t>
- </section>
- </section>
- <section anchor="command-noop" title="noop">
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MUST NOT have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- Used as a keep alive. Flushes outstanding getq/getkq's.
- </t>
- <section anchor="command-noop-example" title="Example">
- <figure>
- <preamble>Noop request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x0a | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x0a
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000000
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key : None
-Value : None
- </artwork>
- </figure>
- <t>
- The response-packet contains no extra data, and the result of the
- operation is signaled through the status code.
- </t>
- </section>
- </section>
-
- <section anchor="command-version" title="version">
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MUST NOT have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- Request the server version.
- </t>
- <t>
- The server responds with a packet containing the version string
- in the body with the following format: "x.y.z"
- </t>
- <section anchor="command-version-example" title="Example">
- <figure>
- <preamble>Version request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x0b | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x0b
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000000
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
- </artwork>
- </figure>
- <figure>
- <preamble>Version response:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x0b | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x31 ('1') | 0x2e ('.') | 0x33 ('3') | 0x2e ('.') |
- +---------------+---------------+---------------+---------------+
- 28| 0x31 ('1') |
- +---------------+
- Total 29 bytes (24 byte header, 5 byte body)
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x0b
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Status (6,7) : 0x0000
-Total body (8-11) : 0x00000005
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key : None
-Value : Textual string "1.3.1"
- </artwork>
- </figure>
- </section>
- </section>
-
- <section anchor="command-append" title="Append, Prepend">
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MUST have key.</t>
- <t>MUST have value.</t>
- </list>
- </t>
-
- <t>
- These commands will either append or prepend the specified
- value to the requested key.
- </t>
-
- <section anchor="command-append-example" title="Example">
- <t>The following example appends '!' to the 'Hello' key.</t>
- <figure>
- <preamble>Append request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x0e | 0x00 | 0x05 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x06 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
- +---------------+---------------+---------------+---------------+
- 28| 0x6f ('o') | 0x21 ('!') |
- +---------------+---------------+
- Total 30 bytes (24 byte header, 5 byte key, 1 byte value)
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x0e
-Key length (2,3) : 0x0005
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000006
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key (24-28): The textual string "Hello"
-Value (29) : "!"
- </artwork>
- </figure>
- <t>
- The response-packet contains no extra data, and the result of the
- operation is signaled through the status code.
- </t>
- </section>
- </section>
-
- <section anchor="command-stat" title="Stat">
- <t>
- <list style="empty">
- <t>MUST NOT have extras.</t>
- <t>MAY have key.</t>
- <t>MUST NOT have value.</t>
- </list>
- </t>
-
- <t>
- Request server statistics. Without a key specified the server will
- respond with a "default" set of statistics information. Each piece
- of statistical information is returned in its own packet (key
- contains the name of the statistical item and the body contains the
- value in ASCII format). The sequence of return packets is terminated
- with a packet that contains no key and no value.
- </t>
- <section anchor="command-stat-example" title="Example">
- <t>The following example requests all statistics from the server</t>
- <figure>
- <preamble>Stat request:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x80 | 0x10 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- Total 24 bytes
-
-Field (offset) (value)
-Magic (0) : 0x80
-Opcode (1) : 0x10
-Key length (2,3) : 0x0000
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Reserved (6,7) : 0x0000
-Total body (8-11) : 0x00000000
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Extras : None
-Key : None
-Value : None
- </artwork>
- </figure>
- <t>
- The server will send each value in a separate packet with
- an "empty" packet (no key / no value) to terminate the sequence.
- Each of the response packets look like the following example:
- </t>
- <figure>
- <preamble>Stat response:</preamble>
- <artwork>
- Byte/ 0 | 1 | 2 | 3 |
- / | | | |
- |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
- +---------------+---------------+---------------+---------------+
- 0| 0x81 | 0x10 | 0x00 | 0x03 |
- +---------------+---------------+---------------+---------------+
- 4| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 8| 0x00 | 0x00 | 0x00 | 0x07 |
- +---------------+---------------+---------------+---------------+
- 12| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 16| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 20| 0x00 | 0x00 | 0x00 | 0x00 |
- +---------------+---------------+---------------+---------------+
- 24| 0x70 ('p') | 0x69 ('i') | 0x64 ('d') | 0x33 ('3') |
- +---------------+---------------+---------------+---------------+
- 28| 0x30 ('0') | 0x37 ('7') | 0x38 ('8') |
- +---------------+---------------+---------------+
- Total 31 bytes (24 byte header, 3 byte key, 4 byte body)
-
-Field (offset) (value)
-Magic (0) : 0x81
-Opcode (1) : 0x10
-Key length (2,3) : 0x0003
-Extra length (4) : 0x00
-Data type (5) : 0x00
-Status (6,7) : 0x0000
-Total body (8-11) : 0x00000007
-Opaque (12-15): 0x00000000
-CAS (16-23): 0x0000000000000000
-Exstras : None
-Key : The textual string "pid"
-Value : The textual string "3078"
- </artwork>
- </figure>
- </section>
- </section>
- </section>
- <section anchor="security" title="Security Considerations">
- <t>
- Memcache has no authentication or security layers whatsoever. It is
- RECOMMENDED that memcache be deployed strictly on closed, protected,
- back-end networks within a single data center, within a single cluster of
- servers, or even on a single host, providing shared caching for multiple
- applications. Memcache MUST NOT be made available on a public network.
- </t>
- </section>
-
- </middle>
-
- <back>
- <references title="Normative References">
- <reference anchor="LJ">
- <front>
- <title>LJ NEEDS MOAR SPEED</title>
- <author fullname="Brad Fitzpatrick">
- <organization>Danga Interactive</organization>
- </author>
- <date day="5" month="10" year="1999" />
- <abstract>
- <t>http://www.livejournal.com/</t>
- </abstract>
- </front>
- </reference>
- <dwdrfc-ref anchor="KEYWORDS" src='http://xml.resource.org/public/rfc/bibxml/reference.RFC.2119.xml'/>
- </references>
-
- <section anchor="acknowledgments" title="Acknowledgments">
- <t>
- Thanks to Brad Fitzpatrick, Anatoly Vorobey, Steven Grimm, and Dustin
- Sallings, for their work on the memcached server.
- </t>
-
- <t>
- Thanks to Sean Chittenden, Jonathan Steinert, Brian Aker, Evan Martin,
- Nathan Neulinger, Eric Hodel, Michael Johnson, Paul Querna, Jamie
- McCarthy, Philip Neustrom, Andrew O'Brien, Josh Rotenberg, Robin H.
- Johnson, Tim Yardley, Paolo Borelli, Eli Bingham, Jean-Francois
- Bustarret, Paul G, Paul Lindner, Alan Kasindorf, Chris Goffinet, Tomash
- Brechko, and others for their work reporting bugs and maintaining
- memcached client libraries and bindings in many languages.
- </t>
- </section>
- </back>
-
-</rfc>
-
+++ /dev/null
-Protocol
---------
-
-Clients of memcached communicate with server through TCP connections.
-(A UDP interface is also available; details are below under "UDP
-protocol.") A given running memcached server listens on some
-(configurable) port; clients connect to that port, send commands to
-the server, read responses, and eventually close the connection.
-
-There is no need to send any command to end the session. A client may
-just close the connection at any moment it no longer needs it. Note,
-however, that clients are encouraged to cache their connections rather
-than reopen them every time they need to store or retrieve data. This
-is because memcached is especially designed to work very efficiently
-with a very large number (many hundreds, more than a thousand if
-necessary) of open connections. Caching connections will eliminate the
-overhead associated with establishing a TCP connection (the overhead
-of preparing for a new connection on the server side is insignificant
-compared to this).
-
-There are two kinds of data sent in the memcache protocol: text lines
-and unstructured data. Text lines are used for commands from clients
-and responses from servers. Unstructured data is sent when a client
-wants to store or retrieve data. The server will transmit back
-unstructured data in exactly the same way it received it, as a byte
-stream. The server doesn't care about byte order issues in
-unstructured data and isn't aware of them. There are no limitations on
-characters that may appear in unstructured data; however, the reader
-of such data (either a client or a server) will always know, from a
-preceding text line, the exact length of the data block being
-transmitted.
-
-Text lines are always terminated by \r\n. Unstructured data is _also_
-terminated by \r\n, even though \r, \n or any other 8-bit characters
-may also appear inside the data. Therefore, when a client retrieves
-data from a server, it must use the length of the data block (which it
-will be provided with) to determine where the data block ends, and not
-the fact that \r\n follows the end of the data block, even though it
-does.
-
-Keys
-----
-
-Data stored by memcached is identified with the help of a key. A key
-is a text string which should uniquely identify the data for clients
-that are interested in storing and retrieving it. Currently the
-length limit of a key is set at 250 characters (of course, normally
-clients wouldn't need to use such long keys); the key must not include
-control characters or whitespace.
-
-Commands
---------
-
-There are three types of commands.
-
-Storage commands (there are six: "set", "add", "replace", "append"
-"prepend" and "cas") ask the server to store some data identified by a
-key. The client sends a command line, and then a data block; after
-that the client expects one line of response, which will indicate
-success or failure.
-
-Retrieval commands (there are two: "get" and "gets") ask the server to
-retrieve data corresponding to a set of keys (one or more keys in one
-request). The client sends a command line, which includes all the
-requested keys; after that for each item the server finds it sends to
-the client one response line with information about the item, and one
-data block with the item's data; this continues until the server
-finished with the "END" response line.
-
-All other commands don't involve unstructured data. In all of them,
-the client sends one command line, and expects (depending on the
-command) either one line of response, or several lines of response
-ending with "END" on the last line.
-
-A command line always starts with the name of the command, followed by
-parameters (if any) delimited by whitespace. Command names are
-lower-case and are case-sensitive.
-
-Expiration times
-----------------
-
-Some commands involve a client sending some kind of expiration time
-(relative to an item or to an operation requested by the client) to
-the server. In all such cases, the actual value sent may either be
-Unix time (number of seconds since January 1, 1970, as a 32-bit
-value), or a number of seconds starting from current time. In the
-latter case, this number of seconds may not exceed 60*60*24*30 (number
-of seconds in 30 days); if the number sent by a client is larger than
-that, the server will consider it to be real Unix time value rather
-than an offset from current time.
-
-
-Error strings
--------------
-
-Each command sent by a client may be answered with an error string
-from the server. These error strings come in three types:
-
-- "ERROR\r\n"
-
- means the client sent a nonexistent command name.
-
-- "CLIENT_ERROR <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. <error> is a
- human-readable error string.
-
-- "SERVER_ERROR <error>\r\n"
-
- means some sort of server error prevents the server from carrying
- out the command. <error> 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:
-
-<command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
-cas <key> <flags> <exptime> <bytes> <cas unique> [noreply]\r\n
-
-- <command name> 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."
-
-- <key> is the key under which the client asks to store the data
-
-- <flags> 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.
-
-- <exptime> 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).
-
-- <bytes> is the number of bytes in the data block to follow, *not*
- including the delimiting \r\n. <bytes> may be zero (in which case
- it's followed by an empty data block).
-
-- <cas unique> 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:
-
-<data block>\r\n
-
-- <data block> is a chunk of arbitrary 8-bit data of length <bytes>
- from the previous line.
-
-After sending the command line and the data blockm the client awaits
-the reply, which may be:
-
-- "STORED\r\n", to indicate success.
-
-- "NOT_STORED\r\n" to indicate the data was not stored, but not
-because of an error. This normally means that the
-condition for an "add" or a "replace" command wasn't met.
-
-- "EXISTS\r\n" to indicate that the item you are trying to store with
-a "cas" command has been modified since you last fetched it.
-
-- "NOT_FOUND\r\n" to indicate that the item you are trying to store
-with a "cas" command did not exist.
-
-
-Retrieval command:
-------------------
-
-The retrieval commands "get" and "gets" operates like this:
-
-get <key>*\r\n
-gets <key>*\r\n
-
-- <key>* 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 <key> <flags> <bytes> [<cas unique>]\r\n
-<data block>\r\n
-
-- <key> is the key for the item being sent
-
-- <flags> is the flags value set by the storage command
-
-- <bytes> is the length of the data block to follow, *not* including
- its delimiting \r\n
-
-- <cas unique> is a unique 64-bit integer that uniquely identifies
- this specific item.
-
-- <data block> 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 <key> [noreply]\r\n
-
-- <key> 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 confusing). 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 <key> <value> [noreply]\r\n
-
-or
-
-decr <key> <value> [noreply]\r\n
-
-- <key> is the key of the item the client wishes to change
-
-- <value> 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
-
-- <value>\r\n , where <value> 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 <key> <exptime> [noreply]\r\n
-
-- <key> is the key of the item the client wishes the server to delete
-
-- <exptime> is expiration time. Works the same as with the update commands
- (set/add/etc). This replaces the existing expiration time. If an existing
- item were to expire in 10 seconds, but then was touched with an
- expiration time of "20", the item would then expire in 20 seconds.
-
-- "noreply" optional parameter instructs the server to not send the
- reply. See the note in Storage commands regarding malformed
- requests.
-
-The response line to this command can be one of:
-
-- "TOUCHED\r\n" to indicate success
-
-- "NOT_FOUND\r\n" to indicate that the item with this key was not
- found.
-
-Slabs Reassign
---------------
-
-NOTE: This command is subject to change as of this writing.
-
-The slabs reassign command is used to redistribute memory once a running
-instance has hit its limit. It might be desireable to have memory laid out
-differently than was automatically assigned after the server started.
-
-slabs reassign <source class> <dest class>\r\n
-
-- <source class> 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"
-
-- <dest 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 run the builtin slow algorithm to choose pages to move
-
-- <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.
-
-Statistics
-----------
-
-The command "stats" is used to query the server about statistics it
-maintains and other internal data. It has two forms. Without
-arguments:
-
-stats\r\n
-
-it causes the server to output general-purpose statistics and
-settings, documented below. In the other form it has some arguments:
-
-stats <args>\r\n
-
-Depending on <args>, various internal data is sent by the server. The
-kinds of arguments and the data sent are not documented in this version
-of the protocol, and are subject to change for the convenience of
-memcache developers.
-
-
-General-purpose statistics
---------------------------
-
-Upon receiving the "stats" command without arguments, the server sents
-a number of lines which look like this:
-
-STAT <name> <value>\r\n
-
-The server terminates this list with the line
-
-END\r\n
-
-In each line of statistics, <name> is the name of this statistic, and
-<value> is the data. The following is the list of all names sent in
-response to the "stats" command, together with the type of the value
-sent for this name, and the meaning of the value.
-
-In the type column below, "32u" means a 32-bit unsigned integer, "64u"
-means a 64-bit unsigned integer. '32u.32u' means two 32-bit unsigned
-integers separated by a colon (treat this as a floating point number).
-
-|-----------------------+---------+-------------------------------------------|
-| Name | Type | Meaning |
-|-----------------------+---------+-------------------------------------------|
-| pid | 32u | Process id of this server process |
-| uptime | 32u | Number of secs since the server started |
-| time | 32u | current UNIX time according to the server |
-| version | string | Version string of this server |
-| pointer_size | 32 | Default size of pointers on the host OS |
-| | | (generally 32 or 64) |
-| rusage_user | 32u.32u | Accumulated user time for this process |
-| | | (seconds:microseconds) |
-| rusage_system | 32u.32u | Accumulated system time for this process |
-| | | (seconds:microseconds) |
-| curr_items | 32u | Current number of items stored |
-| total_items | 32u | Total number of items stored since |
-| | | the server started |
-| bytes | 64u | Current number of bytes used |
-| | | to store items |
-| curr_connections | 32u | Number of open connections |
-| total_connections | 32u | Total number of connections opened since |
-| | | the server started running |
-| connection_structures | 32u | Number of connection structures allocated |
-| | | by the server |
-| reserved_fds | 32u | Number of misc fds used internally |
-| cmd_get | 64u | Cumulative number of retrieval reqs |
-| cmd_set | 64u | Cumulative number of storage reqs |
-| cmd_flush | 64u | Cumulative number of flush reqs |
-| cmd_touch | 64u | Cumulative number of touch reqs |
-| get_hits | 64u | Number of keys that have been requested |
-| | | and found present |
-| get_misses | 64u | Number of items that have been requested |
-| | | and not found |
-| delete_misses | 64u | Number of deletions reqs for missing keys |
-| delete_hits | 64u | Number of deletion reqs resulting in |
-| | | an item being removed. |
-| incr_misses | 64u | Number of incr reqs against missing keys. |
-| incr_hits | 64u | Number of successful incr reqs. |
-| decr_misses | 64u | Number of decr reqs against missing keys. |
-| decr_hits | 64u | Number of successful decr reqs. |
-| cas_misses | 64u | Number of CAS reqs against missing keys. |
-| cas_hits | 64u | Number of successful CAS reqs. |
-| cas_badval | 64u | Number of CAS reqs for which a key was |
-| | | found, but the CAS value did not match. |
-| touch_hits | 64u | Numer of keys that have been touched with |
-| | | a new expiration time |
-| touch_misses | 64u | Numer of items that have been touched and |
-| | | not found |
-| auth_cmds | 64u | Number of authentication commands |
-| | | handled, success or failure. |
-| auth_errors | 64u | Number of failed authentications. |
-| evictions | 64u | Number of valid items removed from cache |
-| | | to free memory for new items |
-| reclaimed | 64u | Number of times an entry was stored using |
-| | | memory from an expired entry |
-| bytes_read | 64u | Total number of bytes read by this server |
-| | | from network |
-| bytes_written | 64u | Total number of bytes sent by this server |
-| | | to network |
-| limit_maxbytes | 32u | Number of bytes this server is allowed to |
-| | | use for storage. |
-| threads | 32u | Number of worker threads requested. |
-| | | (see doc/threads.txt) |
-| conn_yields | 64u | Number of times any connection yielded to |
-| | | another due to hitting the -R limit. |
-| hash_power_level | 32u | Current size multiplier for hash table |
-| hash_bytes | 64u | Bytes currently used by hash tables |
-| hash_is_expanding | bool | Indicates if the hash table is being |
-| | | grown to a new size |
-| expired_unfetched | 64u | Items pulled from LRU that were never |
-| | | touched by get/incr/append/etc before |
-| | | expiring |
-| evicted_unfetched | 64u | Items evicted from LRU that were never |
-| | | touched by get/incr/append/etc. |
-| slab_reassign_running | bool | If a slab page is being moved |
-| slabs_moved | 64u | Total slab pages moved |
-|-----------------------+---------+-------------------------------------------|
-
-Settings statistics
--------------------
-CAVEAT: This section describes statistics which are subject to change in the
-future.
-
-The "stats" command with the argument of "settings" returns details of
-the settings of the running memcached. This is primarily made up of
-the results of processing commandline options.
-
-Note that these are not guaranteed to return in any specific order and
-this list may not be exhaustive. Otherwise, this returns like any
-other stats command.
-
-|-------------------+----------+----------------------------------------------|
-| Name | Type | Meaning |
-|-------------------+----------+----------------------------------------------|
-| maxbytes | size_t | Maximum number of bytes allows in this cache |
-| maxconns | 32 | Maximum number of clients allowed. |
-| tcpport | 32 | TCP listen port. |
-| udpport | 32 | UDP listen port. |
-| inter | string | Listen interface. |
-| verbosity | 32 | 0 = none, 1 = some, 2 = lots |
-| oldest | 32u | Age of the oldest honored object. |
-| evictions | on/off | When off, LRU evictions are disabled. |
-| domain_socket | string | Path to the domain socket (if any). |
-| umask | 32 (oct) | umask for the creation of the domain socket. |
-| growth_factor | float | Chunk size growth factor. |
-| chunk_size | 32 | Minimum space allocated for key+value+flags. |
-| num_threads | 32 | Number of threads (including dispatch). |
-| stat_key_prefix | char | Stats prefix separator character. |
-| detail_enabled | bool | If yes, stats detail is enabled. |
-| reqs_per_event | 32 | Max num IO ops processed within an event. |
-| cas_enabled | bool | When no, CAS is not enabled for this server. |
-| tcp_backlog | 32 | TCP listen backlog. |
-| auth_enabled_sasl | yes/no | SASL auth requested and enabled. |
-| item_size_max | size_t | maximum item size |
-| maxconns_fast | bool | If fast disconnects are enabled |
-| hashpower_init | 32 | Starting size multiplier for hash table |
-| slab_reassign | bool | Whether slab page reassignment is allowed |
-| slab_automove | bool | Whether slab page automover is enabled |
-|-------------------+----------+----------------------------------------------|
-
-
-Item statistics
----------------
-CAVEAT: This section describes statistics which are subject to change in the
-future.
-
-The "stats" command with the argument of "items" returns information about
-item storage per slab class. The data is returned in the format:
-
-STAT items:<slabclass>:<stat> <value>\r\n
-
-The server terminates this list with the line
-
-END\r\n
-
-The slabclass aligns with class ids used by the "stats slabs" command. Where
-"stats slabs" describes size and memory usage, "stats items" shows higher
-level information.
-
-The following item values are defined as of writing.
-
-Name Meaning
-------------------------------
-number Number of items presently stored in this class. Expired
- items are not automatically excluded.
-age Age of the oldest item in the LRU.
-evicted Number of times an item had to be evicted from the LRU
- before it expired.
-evicted_nonzero Number of times an item which had an explicit expire
- time set had to be evicted from the LRU before it
- expired.
-evicted_time Seconds since the last access for the most recent item
- evicted from this class. Use this to judge how
- recently active your evicted data is.
-outofmemory Number of times the underlying slab class was unable to
- store a new item. This means you are running with -M or
- an eviction failed.
-tailrepairs Number of times we self-healed a slab with a refcount
- leak. If this counter is increasing a lot, please
- report your situation to the developers.
-reclaimed Number of times an entry was stored using memory from
- an expired entry.
-expired_unfetched Number of expired items reclaimed from the LRU which
- were never touched after being set.
-evicted_unfetched Number of valid items evicted from the LRU which were
- never touched after being set.
-
-Note this will only display information about slabs which exist, so an empty
-cache will return an empty set.
-
-
-Item size statistics
---------------------
-CAVEAT: This section describes statistics which are subject to change in the
-future.
-
-The "stats" command with the argument of "sizes" returns information about the
-general size and count of all items stored in the cache.
-WARNING: This command WILL lock up your cache! It iterates over *every item*
-and examines the size. While the operation is fast, if you have many items
-you could prevent memcached from serving requests for several seconds.
-
-The data is returned in the following format:
-
-<size> <count>\r\n
-
-The server terminates this list with the line
-
-END\r\n
-
-'size' is an approximate size of the item, within 32 bytes.
-'count' is the amount of items that exist within that 32-byte range.
-
-This is essentially a display of all of your items if there was a slab class
-for every 32 bytes. You can use this to determine if adjusting the slab growth
-factor would save memory overhead. For example: generating more classes in the
-lower range could allow items to fit more snugly into their slab classes, if
-most of your items are less than 200 bytes in size.
-
-
-Slab statistics
----------------
-CAVEAT: This section describes statistics which are subject to change in the
-future.
-
-The "stats" command with the argument of "slabs" returns information about
-each of the slabs created by memcached during runtime. This includes per-slab
-information along with some totals. The data is returned in the format:
-
-STAT <slabclass>:<stat> <value>\r\n
-STAT <stat> <value>\r\n
-
-The server terminates this list with the line
-
-END\r\n
-
-|-----------------+----------------------------------------------------------|
-| Name | Meaning |
-|-----------------+----------------------------------------------------------|
-| chunk_size | The amount of space each chunk uses. One item will use |
-| | one chunk of the appropriate size. |
-| chunks_per_page | How many chunks exist within one page. A page by |
-| | default is less than or equal to one megabyte in size. |
-| | Slabs are allocated by page, then broken into chunks. |
-| total_pages | Total number of pages allocated to the slab class. |
-| total_chunks | Total number of chunks allocated to the slab class. |
-| get_hits | Total number of get requests serviced by this class. |
-| cmd_set | Total number of set requests storing data in this class. |
-| delete_hits | Total number of successful deletes from this class. |
-| incr_hits | Total number of incrs modifying this class. |
-| decr_hits | Total number of decrs modifying this class. |
-| cas_hits | Total number of CAS commands modifying this class. |
-| cas_badval | Total number of CAS commands that failed to modify a |
-| | value due to a bad CAS id. |
-| touch_hits | Total number of touches serviced by this class. |
-| used_chunks | How many chunks have been allocated to items. |
-| free_chunks | Chunks not yet allocated to items, or freed via delete. |
-| free_chunks_end | Number of free chunks at the end of the last allocated |
-| | page. |
-| mem_requested | Number of bytes requested to be stored in this slab[*]. |
-| active_slabs | Total number of slab classes allocated. |
-| total_malloced | Total amount of memory allocated to slab pages. |
-|-----------------+----------------------------------------------------------|
-
-* Items are stored in a slab that is the same size or larger than the
- item. mem_requested shows the size of all items within a
- slab. (total_chunks * chunk_size) - mem_requested shows memory
- wasted in a slab class. If you see a lot of waste, consider tuning
- the slab factor.
-
-Other commands
---------------
-
-"flush_all" is a command with an optional numeric argument. It always
-succeeds, and the server sends "OK\r\n" in response (unless "noreply"
-is given as the last parameter). Its effect is to invalidate all
-existing items immediately (by default) or after the expiration
-specified. After invalidation none of the items will be returned in
-response to a retrieval command (unless it's stored again under the
-same key *after* flush_all has invalidated the items). flush_all
-doesn't actually free all the memory taken up by existing items; that
-will happen gradually as new items are stored. The most precise
-definition of what flush_all does is the following: it causes all
-items whose update time is earlier than the time at which flush_all
-was set to be executed to be ignored for retrieval purposes.
-
-The intent of flush_all with a delay, was that in a setting where you
-have a pool of memcached servers, and you need to flush all content,
-you have the option of not resetting all memcached servers at the
-same time (which could e.g. cause a spike in database load with all
-clients suddenly needing to recreate content that would otherwise
-have been found in the memcached daemon).
-
-The delay option allows you to have them reset in e.g. 10 second
-intervals (by passing 0 to the first, 10 to the second, 20 to the
-third, etc. etc.).
-
-
-"version" is a command with no arguments:
-
-version\r\n
-
-In response, the server sends
-
-"VERSION <version>\r\n", where <version> is the version string for the
-server.
-
-"verbosity" is a command with a numeric argument. It always succeeds,
-and the server sends "OK\r\n" in response (unless "noreply" is given
-as the last parameter). Its effect is to set the verbosity level of
-the logging output.
-
-"quit" is a command with no arguments:
-
-quit\r\n
-
-Upon receiving this command, the server closes the
-connection. However, the client may also simply close the connection
-when it no longer needs it, without issuing this command.
-
-
-UDP protocol
-------------
-
-For very large installations where the number of clients is high enough
-that the number of TCP connections causes scaling difficulties, there is
-also a UDP-based interface. The UDP interface does not provide guaranteed
-delivery, so should only be used for operations that aren't required to
-succeed; typically it is used for "get" requests where a missing or
-incomplete response can simply be treated as a cache miss.
-
-Each UDP datagram contains a simple frame header, followed by data in the
-same format as the TCP protocol described above. In the current
-implementation, requests must be contained in a single UDP datagram, but
-responses may span several datagrams. (The only common requests that would
-span multiple datagrams are huge multi-key "get" requests and "set"
-requests, both of which are more suitable to TCP transport for reliability
-reasons anyway.)
-
-The frame header is 8 bytes long, as follows (all values are 16-bit integers
-in network byte order, high byte first):
-
-0-1 Request ID
-2-3 Sequence number
-4-5 Total number of datagrams in this message
-6-7 Reserved for future use; must be 0
-
-The request ID is supplied by the client. Typically it will be a
-monotonically increasing value starting from a random seed, but the client
-is free to use whatever request IDs it likes. The server's response will
-contain the same ID as the incoming request. The client uses the request ID
-to differentiate between responses to outstanding requests if there are
-several pending from the same server; any datagrams with an unknown request
-ID are probably delayed responses to an earlier request and should be
-discarded.
-
-The sequence number ranges from 0 to n-1, where n is the total number of
-datagrams in the message. The client should concatenate the payloads of the
-datagrams for a given response in sequence number order; the resulting byte
-stream will contain a complete response in the same format as the TCP
-protocol (including terminating \r\n sequences).
+++ /dev/null
-To build the documentation you need xml2rfc ( http://xml.resource.org/ ).
+++ /dev/null
-<?xml version='1.0' encoding='UTF-8'?>
-
-<reference anchor='RFC0768'>
-
-<front>
-<title>User Datagram Protocol</title>
-<author initials='J.' surname='Postel' fullname='J. Postel'>
-<organization>University of Southern California (USC)/Information Sciences Institute</organization>
-<address>
-<postal>
-<street>4676 Admiralty Way</street>
-<city>Marina del Rey</city>
-<region>CA</region>
-<code>90291</code>
-<country>US</country></postal>
-<phone>+1 213 822 1511</phone></address></author>
-<date year='1980' day='28' month='August' /></front>
-
-<seriesInfo name='STD' value='6' />
-<seriesInfo name='RFC' value='768' />
-<format type='TXT' octets='5896' target='ftp://ftp.isi.edu/in-notes/rfc768.txt' />
-</reference>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
- <xsl:output method="xml"/>
-
- <xsl:template name='noinc'>
- <xsl:apply-templates mode='noinc'/>
- </xsl:template>
-
- <xsl:template match="*" mode='noinc'>
- <xsl:copy>
- <xsl:apply-templates select='@*' mode='noinc'/>
- <xsl:apply-templates mode='noinc'/>
- </xsl:copy>
- </xsl:template>
- <xsl:template match='@*' mode='noinc'>
- <xsl:attribute name='{name()}'>
- <xsl:value-of select='.'/>
- </xsl:attribute>
- </xsl:template>
-
- <xsl:template match='processing-instruction("rfc")' mode='noinc'>
- <xsl:choose>
- <xsl:when test='substring-before(.,"=") = "include"'>
- <xsl:call-template name='include-pi'>
- <xsl:with-param name='include-href'>
- <xsl:value-of select="translate( substring-after( ., '=' ), '" ', '' )"/><xsl:text>.xml</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:when>
- <xsl:otherwise>
- <xsl:copy-of select='.'/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:template>
-
- <xsl:template name='include-pi'>
- <xsl:param name='include-href'/>
- <xsl:apply-templates select="document( $include-href )" mode='noinc'/>
- </xsl:template>
-
- <xsl:template match='dwdrfc-ref' mode='noinc'>
- <xsl:param name='include-href'>
- <xsl:choose>
- <xsl:when test='starts-with( @src, "http://" )'>
- <xsl:value-of select='@src'/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:text>http://xml.resource.org/public/rfc/</xsl:text>
- <xsl:value-of select='@src'/>
- <xsl:text>.xml</xsl:text>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:param>
- <reference anchor="{@anchor}">
- <xsl:apply-templates select="document( $include-href )" mode="refrename"/>
- </reference>
- </xsl:template>
-
- <xsl:template match='*' mode='refrename'>
- <xsl:apply-templates mode='refrename'/>
- </xsl:template>
-
- <xsl:template match='reference' mode='refrename'>
- <xsl:apply-templates mode='noinc'/>
- </xsl:template>
-
- <xsl:template match='/'>
- <xsl:call-template name='noinc'/>
- </xsl:template>
-
- <xsl:template name='output'>
- <xsl:param name='foo'/>
- <xsl:copy-of select='$foo'/>
- <xsl:apply-templates select='$foo'/>
- </xsl:template>
-
- <xsl:template match='*' mode='output'>
- <element>
- <xsl:value-of select='name()'/>
- <xsl:apply-templates mode='output'/>
- </element>
- </xsl:template>
- <xsl:template match='text()' mode='output'/>
-
- <!-- Reference checking attributes stripped here. -->
- <xsl:template match='references' mode='noinc'>
- <xsl:element name='references'>
- <xsl:attribute name='title'>
- <xsl:value-of select='@title'/>
- </xsl:attribute>
- <xsl:apply-templates mode='noinc'/>
- </xsl:element>
- </xsl:template>
-
- <xsl:template match='xref' mode='noinc'>
- <xsl:element name='xref'>
- <xsl:attribute name='target'>
- <xsl:value-of select='@target'/>
- </xsl:attribute>
- <xsl:apply-templates mode='noinc'/>
- </xsl:element>
- </xsl:template>
-
-</xsl:stylesheet>
+++ /dev/null
-<!-- rfc2629-other.ent
-
- Character entity set for RFC-2629 source xml documents.
- There is no need to invoke this directly from
- the source document itself; just invoke the DTD.
-
- These are unknown to (X)HTML, so any converter to these
- formats must either substitute these entities
- or invoke this file from the output file.
-
- Conversion to txt or nroff format will replace
- these entities by the parenthesized text.
-
- Typical invocation:
- <!ENTITY % rfc2629-other
- PUBLIC "-//IETF//ENTITIES Other for RFC 2629//EN"
- "http://xml.resource.org/authoring/rfc2629-other.ent">
- %rfc2629-other;
--->
-
-<!-- Magical -->
-<!-- rfc.number (automatically expanded to content
- of number="..." attribute
- of <rfc> element, or to "XXXX") -->
-
-<!-- ASCII -->
-<!ENTITY excl "!"><!-- U+0021 EXCLAMATION MARK ("!") -->
-<!ENTITY num "#"><!-- U+0023 NUMBER SIGN ("#") -->
-<!ENTITY dollar "$"><!-- U+0024 DOLLAR SIGN ("$") -->
-<!ENTITY percnt "%"><!-- U+0025 PERCENT SIGN ("%") -->
-<!ENTITY lpar "("><!-- U+0028 LEFT PARENTHESIS ("(") -->
-<!ENTITY rpar ")"><!-- U+0029 RIGHT PARENTHESIS (")") -->
-<!ENTITY ast "*"><!-- U+002A ASTERISK ("*") -->
-<!ENTITY plus "+"><!-- U+002B PLUS SIGN ("+") -->
-<!ENTITY comma ","><!-- U+002C COMMA (",") -->
-<!ENTITY hyphen "-"><!-- U+002D HYPHEN-MINUS ("-") -->
-<!ENTITY period "."><!-- U+002E FULL STOP (".") -->
-<!ENTITY sol "/"><!-- U+002F SOLIDUS ("/") -->
-<!ENTITY colon ":"><!-- U+003A COLON (":") -->
-<!ENTITY semi ";"><!-- U+003B SEMICOLON (";") -->
-<!ENTITY equals "="><!-- U+003D EQUALS SIGN ("=") -->
-<!ENTITY quest "?"><!-- U+003F QUESTION MARK ("?") -->
-<!ENTITY commat "@"><!-- U+0040 COMMERCIAL AT ("@") -->
-<!ENTITY lsqb "["><!-- U+005B LEFT SQUARE BRACKET ("[") -->
-<!ENTITY bsol "\"><!-- U+005C REVERSE SOLIDUS ("\\") -->
-<!ENTITY rsqb "]"><!-- U+005D RIGHT SQUARE BRACKET ("]") -->
-<!ENTITY circ "^"><!-- U+005E CIRCUMFLEX ACCENT ("^") -->
-<!ENTITY lowbar "_"><!-- U+005F LOW LINE ("_") -->
-<!ENTITY grave "`"><!-- U+0060 GRAVE ACCENT ("`") -->
-<!ENTITY lcub "{"><!-- U+007B LEFT CURLY BRACKET ("{") -->
-<!ENTITY verbar "|"><!-- U+007C VERTICAL LINE ("|") -->
-<!ENTITY rcub "}"><!-- U+007D RIGHT CURLY BRACKET ("}") -->
-
-<!-- Useful Unicode -->
-<!ENTITY Zcaron "Ž"><!-- U+017D LATIN CAPITAL LETTER Z WITH CARON ("Z") -->
-<!ENTITY zcaron "ž"><!-- U+017E LATIN SMALL LETTER Z WITH CARON ("z") -->
-<!ENTITY dash "‐"><!-- U+2010 HYPHEN ("-") -->
-<!ENTITY nbhy "‑"><!-- U+2011 NON-BREAKING HYPHEN (special "-") -->
-<!ENTITY wj "⁠"><!-- U+2060 WORD JOINER (special "") -->
-
-<!-- EOF -->
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
- <xsl:output method="xml"/>
-
- <xsl:template match='/'>
- <refcheck>
- <xsl:apply-templates select='//xref' mode='missing'/>
- <xsl:apply-templates select='//reference' mode='orphans'/>
- <xsl:apply-templates select='//references[@dwdrfc-type = "norm"]' mode='normcheck'/>
- <xsl:apply-templates select='//xref[@dwdrfc-type = "norm"]' mode='normrefs'/>
- </refcheck>
- </xsl:template>
-
- <xsl:template match='xref' mode='missing'>
- <xsl:param name='refname'>
- <xsl:value-of select='@target'/>
- </xsl:param>
- <xsl:param name='reftgt'>
- <xsl:value-of select='//*[@anchor = $refname]/@anchor'/>
- </xsl:param>
- <xsl:choose>
- <xsl:when test='$reftgt = $refname'/>
- <xsl:otherwise>
- <missing><xsl:value-of select='$refname'/></missing><xsl:text>
- </xsl:text>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:template>
-
- <xsl:template match='xref' mode='normrefs'>
- <xsl:param name='refname'>
- <xsl:value-of select='@target'/>
- </xsl:param>
- <xsl:param name='reftgt'>
- <xsl:value-of select='//references[@dwdrfc-type = "norm"]/*[@anchor = $refname]/@anchor'/>
- </xsl:param>
- <xsl:choose>
- <xsl:when test='$reftgt = $refname'/>
- <xsl:otherwise>
- <missing-norm><xsl:value-of select='$refname'/></missing-norm><xsl:text>
- </xsl:text>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:template>
-
- <xsl:template match='reference' mode='orphans'>
- <xsl:param name='refname'>
- <xsl:value-of select='@anchor'/>
- </xsl:param>
- <xsl:param name='reftgt'>
- <xsl:value-of select='//xref[@target = $refname]/@target'/>
- </xsl:param>
- <xsl:if test='$reftgt != $refname'>
- <orphan><xsl:value-of select='$refname'/></orphan><xsl:text>
- </xsl:text>
- </xsl:if>
-</xsl:template>
-
-<xsl:template match='references' mode='normcheck'>
- <xsl:apply-templates mode='normcheck'/>
-</xsl:template>
-
-<xsl:template match='*' mode='normcheck'>
- <!-- Need to find at least one normative reference -->
- <xsl:param name='refname'>
- <xsl:value-of select='@anchor'/>
- </xsl:param>
- <xsl:param name='reftgt'>
- <xsl:value-of select='//xref[@dwdrfc-type = "norm" and @target = $refname]/@target'/>
- </xsl:param>
- <xsl:if test='$refname != $reftgt'>
- <normchk><xsl:value-of select='$refname'/></normchk><xsl:text>
-</xsl:text>
- </xsl:if>
-</xsl:template>
-
-<xsl:template match='text()' mode='normcheck'/>
-
-</xsl:stylesheet>
+++ /dev/null
-<!-- rfc2629-xhtml.ent
-
- Character entity set for RFC-2629 source xml documents.
- There is no need to invoke this directly from
- the source document itself; just invoke the DTD.
-
- These are known to (X)HTML, so any converter to these
- formats can just leave them as is without having
- to invoke this file from the output file.
-
- Conversion to txt or nroff format will replace
- these entities by the parenthesized text.
-
- Typical invocation:
- <!ENTITY % rfc2629-xhtml
- PUBLIC "-//IETF//ENTITIES XHTML subset for RFC 2629//EN"
- "http://xml.resource.org/authoring/rfc2629-xhtml.ent">
- %rfc2629-xhtml;
--->
-
-<!-- All of ISO Latin 1 -->
-<!ENTITY nbsp " "><!-- U+00A0 NO-BREAK SPACE (special " ") -->
-<!ENTITY iexcl "¡"><!-- U+00A1 INVERTED EXCLAMATION MARK ("!") -->
-<!ENTITY cent "¢"><!-- U+00A2 CENT SIGN ("[cents]") -->
-<!ENTITY pound "£"><!-- U+00A3 POUND SIGN ("GBP") -->
-<!ENTITY curren "¤"><!-- U+00A4 CURRENCY SIGN ("[currency units]") -->
-<!ENTITY yen "¥"><!-- U+00A5 YEN SIGN ("JPY") -->
-<!ENTITY brvbar "¦"><!-- U+00A6 BROKEN BAR ("|") -->
-<!ENTITY sect "§"><!-- U+00A7 SECTION SIGN ("S.") -->
-<!ENTITY uml "¨"><!-- U+00A8 DIAERESIS ('"') -->
-<!ENTITY copy "©"><!-- U+00A9 COPYRIGHT SIGN ("(C)") -->
-<!ENTITY ordf "ª"><!-- U+00AA FEMININE ORDINAL INDICATOR ("a") -->
-<!ENTITY laquo "«"><!-- U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK ("<<") -->
-<!ENTITY not "¬"><!-- U+00AC NOT SIGN ("[not]") -->
-<!ENTITY shy "­"><!-- U+00AD SOFT HYPHEN (ignored "") -->
-<!ENTITY reg "®"><!-- U+00AE REGISTERED SIGN ("(R)") -->
-<!ENTITY macr "¯"><!-- U+00AF MACRON ("_") -->
-<!ENTITY deg "°"><!-- U+00B0 DEGREE SIGN ("o") -->
-<!ENTITY plusmn "±"><!-- U+00B1 PLUS-MINUS SIGN ("+/-") -->
-<!ENTITY sup2 "²"><!-- U+00B2 SUPERSCRIPT TWO ("^2") -->
-<!ENTITY sup3 "³"><!-- U+00B3 SUPERSCRIPT THREE ("^3") -->
-<!ENTITY acute "´"><!-- U+00B4 ACUTE ACCENT ("'") -->
-<!ENTITY micro "µ"><!-- U+00B5 MICRO SIGN ("[micro]") -->
-<!ENTITY para "¶"><!-- U+00B6 PILCROW SIGN ("P.") -->
-<!ENTITY middot "·"><!-- U+00B7 MIDDLE DOT (".") -->
-<!ENTITY cedil "¸"><!-- U+00B8 CEDILLA (",") -->
-<!ENTITY sup1 "¹"><!-- U+00B9 SUPERSCRIPT ONE ("^1") -->
-<!ENTITY ordm "º"><!-- U+00BA MASCULINE ORDINAL INDICATOR ("o") -->
-<!ENTITY raquo "»"><!-- U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK (">>") -->
-<!ENTITY frac14 "¼"><!-- U+00BC VULGAR FRACTION ONE QUARTER ("1/4") -->
-<!ENTITY frac12 "½"><!-- U+00BD VULGAR FRACTION ONE HALF ("1/2") -->
-<!ENTITY frac34 "¾"><!-- U+00BE VULGAR FRACTION THREE QUARTERS ("3/4") -->
-<!ENTITY iquest "¿"><!-- U+00BF INVERTED QUESTION MARK ("?") -->
-<!ENTITY Agrave "À"><!-- U+00C0 LATIN CAPITAL LETTER A WITH GRAVE ("A") -->
-<!ENTITY Aacute "Á"><!-- U+00C1 LATIN CAPITAL LETTER A WITH ACUTE ("A") -->
-<!ENTITY Acirc "Â"><!-- U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX ("A") -->
-<!ENTITY Atilde "Ã"><!-- U+00C3 LATIN CAPITAL LETTER A WITH TILDE ("A") -->
-<!ENTITY Auml "Ä"><!-- U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS ("Ae") -->
-<!ENTITY Aring "Å"><!-- U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE ("Ae") -->
-<!ENTITY AElig "Æ"><!-- U+00C6 LATIN CAPITAL LETTER AE ("AE") -->
-<!ENTITY Ccedil "Ç"><!-- U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA ("C") -->
-<!ENTITY Egrave "È"><!-- U+00C8 LATIN CAPITAL LETTER E WITH GRAVE ("E") -->
-<!ENTITY Eacute "É"><!-- U+00C9 LATIN CAPITAL LETTER E WITH ACUTE ("E") -->
-<!ENTITY Ecirc "Ê"><!-- U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX ("E") -->
-<!ENTITY Euml "Ë"><!-- U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS ("E") -->
-<!ENTITY Igrave "Ì"><!-- U+00CC LATIN CAPITAL LETTER I WITH GRAVE ("I") -->
-<!ENTITY Iacute "Í"><!-- U+00CD LATIN CAPITAL LETTER I WITH ACUTE ("I") -->
-<!ENTITY Icirc "Î"><!-- U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX ("I") -->
-<!ENTITY Iuml "Ï"><!-- U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS ("I") -->
-<!ENTITY ETH "Ð"><!-- U+00D0 LATIN CAPITAL LETTER ETH ("[ETH]") -->
-<!ENTITY Ntilde "Ñ"><!-- U+00D1 LATIN CAPITAL LETTER N WITH TILDE ("N") -->
-<!ENTITY Ograve "Ò"><!-- U+00D2 LATIN CAPITAL LETTER O WITH GRAVE ("O") -->
-<!ENTITY Oacute "Ó"><!-- U+00D3 LATIN CAPITAL LETTER O WITH ACUTE ("O") -->
-<!ENTITY Ocirc "Ô"><!-- U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX ("O") -->
-<!ENTITY Otilde "Õ"><!-- U+00D5 LATIN CAPITAL LETTER O WITH TILDE ("O") -->
-<!ENTITY Ouml "Ö"><!-- U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS ("Oe") -->
-<!ENTITY times "×"><!-- U+00D7 MULTIPLICATION SIGN ("x") -->
-<!ENTITY Oslash "Ø"><!-- U+00D8 LATIN CAPITAL LETTER O WITH STROKE ("Oe") -->
-<!ENTITY Ugrave "Ù"><!-- U+00D9 LATIN CAPITAL LETTER U WITH GRAVE ("U") -->
-<!ENTITY Uacute "Ú"><!-- U+00DA LATIN CAPITAL LETTER U WITH ACUTE ("U") -->
-<!ENTITY Ucirc "Û"><!-- U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX ("U") -->
-<!ENTITY Uuml "Ü"><!-- U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS ("Ue") -->
-<!ENTITY Yacute "Ý"><!-- U+00DD LATIN CAPITAL LETTER Y WITH ACUTE ("Y") -->
-<!ENTITY THORN "Þ"><!-- U+00DE LATIN CAPITAL LETTER THORN ("[THORN]") -->
-<!ENTITY szlig "ß"><!-- U+00DF LATIN SMALL LETTER SHARP S ("ss") -->
-<!ENTITY agrave "à"><!-- U+00E0 LATIN SMALL LETTER A WITH GRAVE ("a") -->
-<!ENTITY aacute "á"><!-- U+00E1 LATIN SMALL LETTER A WITH ACUTE ("a") -->
-<!ENTITY acirc "â"><!-- U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX ("a") -->
-<!ENTITY atilde "ã"><!-- U+00E3 LATIN SMALL LETTER A WITH TILDE ("a") -->
-<!ENTITY auml "ä"><!-- U+00E4 LATIN SMALL LETTER A WITH DIAERESIS ("ae") -->
-<!ENTITY aring "å"><!-- U+00E5 LATIN SMALL LETTER A WITH RING ABOVE ("ae") -->
-<!ENTITY aelig "æ"><!-- U+00E6 LATIN SMALL LETTER AE ("ae") -->
-<!ENTITY ccedil "ç"><!-- U+00E7 LATIN SMALL LETTER C WITH CEDILLA ("c") -->
-<!ENTITY egrave "è"><!-- U+00E8 LATIN SMALL LETTER E WITH GRAVE ("e") -->
-<!ENTITY eacute "é"><!-- U+00E9 LATIN SMALL LETTER E WITH ACUTE ("e") -->
-<!ENTITY ecirc "ê"><!-- U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX ("e") -->
-<!ENTITY euml "ë"><!-- U+00EB LATIN SMALL LETTER E WITH DIAERESIS ("e") -->
-<!ENTITY igrave "ì"><!-- U+00EC LATIN SMALL LETTER I WITH GRAVE ("i") -->
-<!ENTITY iacute "í"><!-- U+00ED LATIN SMALL LETTER I WITH ACUTE ("i") -->
-<!ENTITY icirc "î"><!-- U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX ("i") -->
-<!ENTITY iuml "ï"><!-- U+00EF LATIN SMALL LETTER I WITH DIAERESIS ("i") -->
-<!ENTITY eth "ð"><!-- U+00F0 LATIN SMALL LETTER ETH ("[eth]") -->
-<!ENTITY ntilde "ñ"><!-- U+00F1 LATIN SMALL LETTER N WITH TILDE ("n") -->
-<!ENTITY ograve "ò"><!-- U+00F2 LATIN SMALL LETTER O WITH GRAVE ("o") -->
-<!ENTITY oacute "ó"><!-- U+00F3 LATIN SMALL LETTER O WITH ACUTE ("o") -->
-<!ENTITY ocirc "ô"><!-- U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX ("o") -->
-<!ENTITY otilde "õ"><!-- U+00F5 LATIN SMALL LETTER O WITH TILDE ("o") -->
-<!ENTITY ouml "ö"><!-- U+00F6 LATIN SMALL LETTER O WITH DIAERESIS ("oe") -->
-<!ENTITY divide "÷"><!-- U+00F7 DIVISION SIGN ("/") -->
-<!ENTITY oslash "ø"><!-- U+00F8 LATIN SMALL LETTER O WITH STROKE ("oe") -->
-<!ENTITY ugrave "ù"><!-- U+00F9 LATIN SMALL LETTER U WITH GRAVE ("u") -->
-<!ENTITY uacute "ú"><!-- U+00FA LATIN SMALL LETTER U WITH ACUTE ("u") -->
-<!ENTITY ucirc "û"><!-- U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX ("u") -->
-<!ENTITY uuml "ü"><!-- U+00FC LATIN SMALL LETTER U WITH DIAERESIS ("ue") -->
-<!ENTITY yacute "ý"><!-- U+00FD LATIN SMALL LETTER Y WITH ACUTE ("y") -->
-<!ENTITY thorn "þ"><!-- U+00FE LATIN SMALL LETTER THORN ("[thorn]") -->
-<!ENTITY yuml "ÿ"><!-- U+00FF LATIN SMALL LETTER Y WITH DIAERESIS ("y") -->
-
-<!-- Some of ISO Latin 9 and 10 -->
-<!ENTITY OElig "Œ"><!-- U+0152 LATIN CAPITAL LIGATURE OE ("OE") -->
-<!ENTITY oelig "œ"><!-- U+0153 LATIN SMALL LIGATURE OE ("oe") -->
-<!ENTITY Scaron "Š"><!-- U+0160 LATIN CAPITAL LETTER S WITH CARON ("S") -->
-<!ENTITY scaron "š"><!-- U+0161 LATIN SMALL LETTER S WITH CARON ("s") -->
-<!ENTITY Yuml "Ÿ"><!-- U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS ("Y") -->
-
-<!-- Other Unicode (including some characters from the windows-1252 repertoire) -->
-<!ENTITY fnof "ƒ"><!-- U+0192 LATIN SMALL LETTER F WITH HOOK ("f") -->
-<!ENTITY tilde "˜"><!-- U+02DC SMALL TILDE ("~") -->
-<!ENTITY ensp " "><!-- U+2002 EN SPACE (" ") -->
-<!ENTITY emsp " "><!-- U+2003 EM SPACE (" ") -->
-<!ENTITY thinsp " "><!-- U+2009 THIN SPACE (" ") -->
-<!ENTITY ndash "–"><!-- U+2013 EN DASH ("-") -->
-<!ENTITY mdash "—"><!-- U+2014 EM DASH ("-\u002D") -->
-<!ENTITY lsquo "‘"><!-- U+2018 LEFT SINGLE QUOTATION MARK ("'") -->
-<!ENTITY rsquo "’"><!-- U+2019 RIGHT SINGLE QUOTATION MARK ("'") -->
-<!ENTITY sbquo "‚"><!-- U+201A SINGLE LOW-9 QUOTATION MARK ("'") -->
-<!ENTITY ldquo "“"><!-- U+201C LEFT DOUBLE QUOTATION MARK ('"') -->
-<!ENTITY rdquo "”"><!-- U+201D RIGHT DOUBLE QUOTATION MARK ('"') -->
-<!ENTITY bdquo "„"><!-- U+201E DOUBLE LOW-9 QUOTATION MARK ('"') -->
-<!ENTITY dagger "†"><!-- U+2020 DAGGER ("*!*") -->
-<!ENTITY Dagger "‡"><!-- U+2021 DOUBLE DAGGER ("*!!*") -->
-<!ENTITY bull "•"><!-- U+2022 BULLET ("o") -->
-<!ENTITY hellip "…"><!-- U+2026 HORIZONTAL ELLIPSIS ("...") -->
-<!ENTITY permil "′"><!-- U+2030 PER MILLE SIGN ("[/1000]") -->
-<!ENTITY prime "′"><!-- U+2032 PRIME ("'") -->
-<!ENTITY Prime "″"><!-- U+2033 DOUBLE PRIME ('"') -->
-<!ENTITY lsaquo "‹"><!-- U+2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK ("<") -->
-<!ENTITY rsaquo "›"><!-- U+203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (">") -->
-<!ENTITY frasl "⁄"><!-- U+2044 FRACTION SLASH ("/") -->
-<!ENTITY euro "€"><!-- U+20AC EURO SIGN ("EUR") -->
-<!ENTITY trade "™"><!-- U+2122 TRADE MARK SIGN ("[TM]") -->
-<!ENTITY larr "←"><!-- U+2190 LEFTWARDS ARROW ("<-\u002D") -->
-<!ENTITY rarr "→"><!-- U+2192 RIGHTWARDS ARROW ("\u002D->") -->
-<!ENTITY harr "↔"><!-- U+2194 LEFT RIGHT ARROW ("<->") -->
-<!ENTITY lArr "⇐"><!-- U+21D0 LEFTWARDS DOUBLE ARROW ("<==") -->
-<!ENTITY rArr "⇒"><!-- U+21D2 RIGHTWARDS DOUBLE ARROW ("==>") -->
-<!ENTITY hArr "⇔"><!-- U+21D4 LEFT RIGHT DOUBLE ARROW ("<=>") -->
-<!ENTITY minus "−"><!-- U+2212 MINUS SIGN ("-") -->
-<!ENTITY lowast "∗"><!-- U+2217 ASTERISK OPERATOR ("*") -->
-<!ENTITY le "≤"><!-- U+2264 LESS-THAN OR EQUAL TO ("<=") -->
-<!ENTITY ge "≥"><!-- U+2265 GREATER-THAN OR EQUAL TO (">=") -->
-<!ENTITY lang "〈"><!-- U+2329 LEFT-POINTING ANGLE BRACKET ("<") -->
-<!ENTITY rang "〉"><!-- U+232A RIGHT-POINTING ANGLE BRACKET (">") -->
-
-<!-- EOF -->
+++ /dev/null
-<!--
- revised DTD for the RFC document series, draft of 2007-03-29
- -->
-
-
-<!--
- Typical invocation:
- <!DOCTYPE rfc PUBLIC "-//IETF//DTD RFC 2629//EN"
- "http://xml.resource.org/authoring/rfc2629.dtd" [
- ... dtd subset ...
- ]>
- or
- <!DOCTYPE rfc SYSTEM "rfc2629.dtd" [
- ... dtd subset ...
- ]>
- -->
-
-
-<!--
- Contents
-
- Character entities
-
- DTD data types
-
- The top-level
-
- Front matter
-
- The Body
-
- Back matter
- -->
-
-
-<!--
- Character entities
- -->
-
-
-<!ENTITY % rfc2629-xhtml
- PUBLIC "-//IETF//ENTITIES XHTML subset for RFC 2629//EN"
- "rfc2629-xhtml.ent">
-%rfc2629-xhtml;
-
-<!ENTITY % rfc2629-other
- PUBLIC "-//IETF//ENTITIES Other for RFC 2629//EN"
- "rfc2629-other.ent">
-%rfc2629-other;
-
-
-<!--
- DTD data types:
-
- entity description
- ====== ===============================================
- NUMBER [0-9]+
- NUMBERS a comma-separated list of NUMBER
-
- DAY the day of the month, e.g., "1"
- MONTH the month of the year, e.g., "January"
- YEAR a four-digit year, e.g., "1999"
-
- URI e.g., "http://invisible.net/"
-
- ATEXT/CTEXT printable ASCII text (no line-terminators)
-
- TEXT character data
- -->
-
-
-<!ENTITY % NUMBER "CDATA">
-<!ENTITY % NUMBERS "CDATA">
-
-<!ENTITY % DAY "CDATA">
-<!ENTITY % MONTH "CDATA">
-<!ENTITY % YEAR "CDATA">
-
-<!ENTITY % URI "CDATA">
-
-<!ENTITY % ATEXT "CDATA">
-<!ENTITY % CTEXT "#PCDATA">
-
-<!ENTITY % TEXT "#PCDATA">
-
-<!ENTITY rfc.number "XXXX">
-
-
-<!--
- The top-level
- -->
-
-
-<!--
- attributes for the "rfc" element are supplied by the RFC
- editor. when preparing drafts, authors should leave them blank.
-
- the "seriesNo" attribute is used if the category is, e.g., BCP.
- -->
-<!ELEMENT rfc (front,middle,back?)>
-<!ATTLIST rfc
- number %NUMBER; #IMPLIED
- obsoletes %NUMBERS; ""
- updates %NUMBERS; ""
- category (std|bcp|info|exp|historic)
- #IMPLIED
- seriesNo %NUMBER; #IMPLIED
- ipr (full2026|noDerivativeWorks2026|none
- |full3667|noModification3667|noDerivatives3667
- |full3978|noModification3978|noDerivatives3978)
- #IMPLIED
- iprExtract IDREF #IMPLIED
- submissionType
- (IETF|independent) "IETF"
- docName %ATEXT; #IMPLIED
- xml:lang %ATEXT; "en">
-
-
-<!--
- Front matter
- -->
-
-
-<!ELEMENT front (title,author+,date,area*,workgroup*,keyword*,
- abstract?,note*)>
-
-<!-- the "abbrev" attribute is used for headers, etc. -->
-<!ELEMENT title (%CTEXT;)>
-<!ATTLIST title
- abbrev %ATEXT; #IMPLIED>
-
-<!ELEMENT author (organization,address?)>
-<!ATTLIST author
- initials %ATEXT; #IMPLIED
- surname %ATEXT; #IMPLIED
- fullname %ATEXT; #IMPLIED
- role (editor) #IMPLIED>
-
-<!ELEMENT organization
- (%CTEXT;)>
-<!ATTLIST organization
- abbrev %ATEXT; #IMPLIED>
-
-<!ELEMENT address (postal?,phone?,facsimile?,email?,uri?)>
-
-<!-- this content model should be more strict:
- at most one of each the city, region, code, and country
- elements may be present -->
-<!ELEMENT postal (street+,(city|region|code|country)*)>
-<!ELEMENT street (%CTEXT;)>
-<!ELEMENT city (%CTEXT;)>
-<!ELEMENT region (%CTEXT;)>
-<!ELEMENT code (%CTEXT;)>
-<!ELEMENT country (%CTEXT;)>
-<!ELEMENT phone (%CTEXT;)>
-<!ELEMENT facsimile (%CTEXT;)>
-<!ELEMENT email (%CTEXT;)>
-<!ELEMENT uri (%CTEXT;)>
-
-<!ELEMENT date EMPTY>
-<!ATTLIST date
- day %DAY; #IMPLIED
- month %MONTH; #IMPLIED
- year %YEAR; #IMPLIED>
-
-<!-- meta-data... -->
-<!ELEMENT area (%CTEXT;)>
-<!ELEMENT workgroup (%CTEXT;)>
-<!ELEMENT keyword (%CTEXT;)>
-
-<!ELEMENT abstract (t)+>
-<!ELEMENT note (t)+>
-<!ATTLIST note
- title %ATEXT; #REQUIRED>
-
-
-<!--
- The body
- -->
-
-
-<!-- later on, may be (section+,appendix*,section*) -->
-<!ELEMENT middle (section+)>
-
-<!ELEMENT section ((t|figure|texttable|iref)*,section*)>
-<!ATTLIST section
- anchor ID #IMPLIED
- title %ATEXT; #REQUIRED
- toc (include|exclude|default)
- "default">
-
-<!--
-<!ELEMENT appendix ((t|figure|texttable|iref)*,appendix*)>
-<!ATTLIST appendix
- anchor ID #IMPLIED
- title %ATEXT; #REQUIRED
- toc (include|exclude|default)
- "default">
- -->
-
-<!-- use of <figure/> is deprecated... -->
-<!ELEMENT t (%TEXT;|list|figure|xref|eref|iref|cref|spanx|vspace)*>
-<!ATTLIST t
- anchor ID #IMPLIED
- hangText %ATEXT; #IMPLIED>
-
-<!-- the value of the style attribute is inherited from the closest
- parent -->
-<!ELEMENT list (t+)>
-<!ATTLIST list
- style %ATEXT; #IMPLIED
- hangIndent %NUMBER; #IMPLIED
- counter %ATEXT; #IMPLIED>
-
-<!ELEMENT xref (%CTEXT;)>
-<!ATTLIST xref
- target IDREF #REQUIRED
- pageno (true|false) "false"
- format (counter|title|none|default)
- "default">
-
-<!ELEMENT eref (%CTEXT;)>
-<!ATTLIST eref
- target %URI; #REQUIRED>
-
-<!ELEMENT iref EMPTY>
-<!ATTLIST iref
- item %ATEXT; #REQUIRED
- subitem %ATEXT; ""
- primary (true|false) "false">
-
-<!ELEMENT cref (%CTEXT;)>
-<!ATTLIST cref
- anchor ID #IMPLIED
- source %ATEXT; #IMPLIED>
-
-<!ELEMENT spanx (%CTEXT;)>
-<!ATTLIST spanx
- style %ATEXT; "emph">
-
-<!ELEMENT vspace EMPTY>
-<!ATTLIST vspace
- blankLines %NUMBER; "0">
-
-<!ELEMENT figure (iref*,preamble?,artwork,postamble?)>
-<!ATTLIST figure
- anchor ID #IMPLIED
- title %ATEXT; ""
- src %URI; #IMPLIED
- align (left|center|right) "left"
- alt %ATEXT; ""
- width %ATEXT; ""
- height %ATEXT; "">
-
-<!ELEMENT preamble (%TEXT;|xref|eref|iref|cref|spanx)*>
-<!ELEMENT artwork (%TEXT;)*>
-<!ATTLIST artwork
- xml:space (default|preserve) "preserve"
- name %ATEXT; ""
- type %ATEXT; ""
- src %URI; #IMPLIED
- align (left|center|right) "left"
- alt %ATEXT; ""
- width %ATEXT; ""
- height %ATEXT; "">
-
-<!ELEMENT postamble (%TEXT;|xref|eref|iref|cref|spanx)*>
-
-<!ELEMENT texttable (preamble?,ttcol+,c*,postamble?)>
-<!ATTLIST texttable
- anchor ID #IMPLIED
- title %ATEXT; "">
-<!ELEMENT ttcol (%CTEXT;)>
-<!ATTLIST ttcol
- width %ATEXT; #IMPLIED
- align (left|center|right) "left">
-<!ELEMENT c (%TEXT;|xref|eref|iref|cref|spanx)*>
-
-
-<!--
- Back matter
- -->
-
-
-<!-- sections, if present, are appendices -->
-<!ELEMENT back (references*,section*)>
-
-<!ELEMENT references (reference+)>
-<!ATTLIST references
- title %ATEXT; "References">
-<!ELEMENT reference (front,seriesInfo*,format*,annotation*)>
-<!ATTLIST reference
- anchor ID #IMPLIED
- target %URI; #IMPLIED>
-<!ELEMENT seriesInfo EMPTY>
-<!ATTLIST seriesInfo
- name %ATEXT; #REQUIRED
- value %ATEXT; #REQUIRED>
-<!ELEMENT format EMPTY>
-<!ATTLIST format
- target %URI; #IMPLIED
- type %ATEXT; #REQUIRED
- octets %NUMBER; #IMPLIED>
-<!ELEMENT annotation (%TEXT;|xref|eref|iref|cref|spanx)*>
+++ /dev/null
-#include "memcached.h"
-
-/*
- * This file contains global variables shared across the rest of the
- * memcached codebase. These were originally in memcached.c but had
- * to be removed to make the rest of the object files linkable into
- * the test infrastructure.
- *
- */
-
-/*
- * We keep the current time of day in a global variable that's updated by a
- * timer event. This saves us a bunch of time() system calls (we really only
- * need to get the time once a second, whereas there can be tens of thousands
- * of requests a second) and allows us to use server-start-relative timestamps
- * rather than absolute UNIX timestamps, a space savings on systems where
- * sizeof(time_t) > sizeof(unsigned int).
- */
-volatile rel_time_t current_time;
-
-/** exported globals **/
-struct stats stats;
-struct settings settings;
-struct slab_rebalance slab_rebal;
-volatile int slab_rebalance_signal;
+++ /dev/null
-/* -*- 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:
- * <http://burtleburtle.net/bob/hash/doobs.html>
- * "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"
-
-/*
- * 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 hash(
- const void *key, /* the key to hash */
- size_t length, /* length of the key */
- const uint32_t initval) /* initval */
-{
- 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) + initval;
-
- 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
- * noticably 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 hash( const void *key, size_t length, const uint32_t initval)
-{
- 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) + initval;
-
- 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
- * noticably 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 */
+++ /dev/null
-#ifndef HASH_H
-#define HASH_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t hash(const void *key, size_t length, const uint32_t initval);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* HASH_H */
-
+++ /dev/null
-# vim:ft=automake
-
-noinst_HEADERS+= memcached/assoc.h
-noinst_HEADERS+= memcached/cache.h
-noinst_HEADERS+= memcached/hash.h
-noinst_HEADERS+= memcached/items.h
-noinst_HEADERS+= memcached/memcached.h
-noinst_HEADERS+= memcached/protocol_binary.h
-noinst_HEADERS+= memcached/sasl_defs.h
-noinst_HEADERS+= memcached/slabs.h
-noinst_HEADERS+= memcached/stats.h
-noinst_HEADERS+= memcached/trace.h
-noinst_HEADERS+= memcached/util.h
-
-memcached_memcached_CFLAGS=
-memcached_memcached_LDADD=
-memcached_memcached_SOURCES=
-
-memcached_memcached_SOURCES+=
-memcached_memcached_SOURCES+= memcached/assoc.c
-memcached_memcached_SOURCES+= memcached/cache.c
-memcached_memcached_SOURCES+= memcached/daemon.c
-memcached_memcached_SOURCES+= memcached/hash.c
-memcached_memcached_SOURCES+= memcached/items.c
-memcached_memcached_SOURCES+= memcached/memcached.c
-memcached_memcached_SOURCES+= memcached/slabs.c
-memcached_memcached_SOURCES+= memcached/stats.c
-memcached_memcached_SOURCES+= memcached/thread.c
-memcached_memcached_SOURCES+= memcached/trace.h
-memcached_memcached_SOURCES+= memcached/util.c
-
-memcached_memcached_CFLAGS+= ${PTHREAD_CFLAGS}
-memcached_memcached_CFLAGS+= -DHAVE_CONFIG_H -std=c99
-
-memcached_memcached_LDADD+= $(LIBEVENT_LDFLAGS)
-memcached_memcached_LDADD+= ${PTHREAD_LIBS}
-
-noinst_PROGRAMS+= memcached/memcached
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#include "memcached.h"
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/signal.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <assert.h>
-
-/* Forward Declarations */
-static void item_link_q(item *it);
-static void item_unlink_q(item *it);
-
-/*
- * 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
-
-#define LARGEST_ID POWER_LARGEST
-typedef struct {
- uint64_t evicted;
- uint64_t evicted_nonzero;
- rel_time_t evicted_time;
- uint64_t reclaimed;
- uint64_t outofmemory;
- uint64_t tailrepairs;
- uint64_t expired_unfetched;
- uint64_t evicted_unfetched;
-} itemstats_t;
-
-static item *heads[LARGEST_ID];
-static item *tails[LARGEST_ID];
-static itemstats_t itemstats[LARGEST_ID];
-static unsigned int sizes[LARGEST_ID];
-
-void item_stats_reset(void) {
- mutex_lock(&cache_lock);
- memset(itemstats, 0, sizeof(itemstats));
- mutex_unlock(&cache_lock);
-}
-
-
-/* Get the next CAS id for a new item. */
-uint64_t get_cas_id(void) {
- static uint64_t cas_id = 0;
- return ++cas_id;
-}
-
-/* Enable this for reference-count debugging. */
-#if 0
-# define DEBUG_REFCNT(it,op) \
- fprintf(stderr, "item %x refcnt(%c) %d %c%c%c\n", \
- it, op, it->refcount, \
- (it->it_flags & ITEM_LINKED) ? 'L' : ' ', \
- (it->it_flags & ITEM_SLABBED) ? 'S' : ' ')
-#else
-# define DEBUG_REFCNT(it,op) while(0)
-#endif
-
-/**
- * Generates the variable-sized part of the header for an object.
- *
- * key - The key
- * nkey - The length of the key
- * flags - key flags
- * nbytes - Number of bytes to hold value and addition CRLF terminator
- * suffix - Buffer for the "VALUE" line suffix (flags, size).
- * nsuffix - The length of the suffix is stored here.
- *
- * Returns the total size of the header.
- */
-static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,
- char *suffix, uint8_t *nsuffix) {
- /* suffix is defined at 40 chars elsewhere.. */
- *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2);
- return sizeof(item) + nkey + *nsuffix + nbytes;
-}
-
-/*@null@*/
-item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
- uint8_t nsuffix;
- item *it = NULL;
- char suffix[40];
- size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
- if (settings.use_cas) {
- ntotal += sizeof(uint64_t);
- }
-
- unsigned int id = slabs_clsid(ntotal);
- if (id == 0)
- return 0;
-
- mutex_lock(&cache_lock);
- /* do a quick check if we have any expired items in the tail.. */
- item *search;
- rel_time_t oldest_live = settings.oldest_live;
-
- search = tails[id];
- if (search != NULL && (refcount_incr(&search->refcount) == 2)) {
- if ((search->exptime != 0 && search->exptime < current_time)
- || (search->time <= oldest_live && oldest_live <= current_time)) { // dead by flush
- STATS_LOCK();
- stats.reclaimed++;
- STATS_UNLOCK();
- itemstats[id].reclaimed++;
- if ((search->it_flags & ITEM_FETCHED) == 0) {
- STATS_LOCK();
- stats.expired_unfetched++;
- STATS_UNLOCK();
- itemstats[id].expired_unfetched++;
- }
- it = search;
- slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);
- do_item_unlink_nolock(it, hash(ITEM_key(it), it->nkey, 0));
- /* Initialize the item block: */
- it->slabs_clsid = 0;
- } else if ((it = slabs_alloc(ntotal, id)) == NULL) {
- if (settings.evict_to_free == 0) {
- itemstats[id].outofmemory++;
- mutex_unlock(&cache_lock);
- return NULL;
- }
- itemstats[id].evicted++;
- itemstats[id].evicted_time = current_time - search->time;
- if (search->exptime != 0)
- itemstats[id].evicted_nonzero++;
- if ((search->it_flags & ITEM_FETCHED) == 0) {
- STATS_LOCK();
- stats.evicted_unfetched++;
- STATS_UNLOCK();
- itemstats[id].evicted_unfetched++;
- }
- STATS_LOCK();
- stats.evictions++;
- STATS_UNLOCK();
- it = search;
- slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);
- do_item_unlink_nolock(it, hash(ITEM_key(it), it->nkey, 0));
- /* Initialize the item block: */
- it->slabs_clsid = 0;
-
- /* If we've just evicted an item, and the automover is set to
- * angry bird mode, attempt to rip memory into this slab class.
- * TODO: Move valid object detection into a function, and on a
- * "successful" memory pull, look behind and see if the next alloc
- * would be an eviction. Then kick off the slab mover before the
- * eviction happens.
- */
- if (settings.slab_automove == 2)
- slabs_reassign(-1, id);
- } else {
- refcount_decr(&search->refcount);
- }
- } else {
- /* If the LRU is empty or locked, attempt to allocate memory */
- it = slabs_alloc(ntotal, id);
- if (search != NULL)
- refcount_decr(&search->refcount);
- }
-
- if (it == NULL) {
- itemstats[id].outofmemory++;
- /* Last ditch effort. There was a very rare bug which caused
- * refcount leaks. We leave this just in case they ever happen again.
- * We can reasonably assume no item can stay locked for more than
- * three hours, so if we find one in the tail which is that old,
- * free it anyway.
- */
- if (search != NULL &&
- search->refcount != 2 &&
- search->time + TAIL_REPAIR_TIME < current_time) {
- itemstats[id].tailrepairs++;
- search->refcount = 1;
- do_item_unlink_nolock(search, hash(ITEM_key(search), search->nkey, 0));
- }
- mutex_unlock(&cache_lock);
- return NULL;
- }
-
- assert(it->slabs_clsid == 0);
- assert(it != heads[id]);
-
- /* Item initialization can happen outside of the lock; the item's already
- * been removed from the slab LRU.
- */
- it->refcount = 1; /* the caller will have a reference */
- mutex_unlock(&cache_lock);
- it->next = it->prev = it->h_next = 0;
- it->slabs_clsid = id;
-
- DEBUG_REFCNT(it, '*');
- it->it_flags = settings.use_cas ? ITEM_CAS : 0;
- it->nkey = nkey;
- it->nbytes = nbytes;
- memcpy(ITEM_key(it), key, nkey);
- it->exptime = exptime;
- memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
- it->nsuffix = nsuffix;
- return it;
-}
-
-void item_free(item *it) {
- size_t ntotal = ITEM_ntotal(it);
- unsigned int clsid;
- assert((it->it_flags & ITEM_LINKED) == 0);
- assert(it != heads[it->slabs_clsid]);
- assert(it != tails[it->slabs_clsid]);
- assert(it->refcount == 0);
-
- /* so slab size changer can tell later if item is already free or not */
- clsid = it->slabs_clsid;
- it->slabs_clsid = 0;
- DEBUG_REFCNT(it, 'F');
- slabs_free(it, ntotal, clsid);
-}
-
-/**
- * Returns true if an item will fit in the cache (its size does not exceed
- * the maximum for a cache entry.)
- */
-bool item_size_ok(const size_t nkey, const int flags, const int nbytes) {
- char prefix[40];
- uint8_t nsuffix;
-
- size_t ntotal = item_make_header(nkey + 1, flags, nbytes,
- prefix, &nsuffix);
- if (settings.use_cas) {
- ntotal += sizeof(uint64_t);
- }
-
- return slabs_clsid(ntotal) != 0;
-}
-
-static void item_link_q(item *it) { /* item is the new head */
- item **head, **tail;
- assert(it->slabs_clsid < LARGEST_ID);
- assert((it->it_flags & ITEM_SLABBED) == 0);
-
- head = &heads[it->slabs_clsid];
- tail = &tails[it->slabs_clsid];
- assert(it != *head);
- assert((*head && *tail) || (*head == 0 && *tail == 0));
- it->prev = 0;
- it->next = *head;
- if (it->next) it->next->prev = it;
- *head = it;
- if (*tail == 0) *tail = it;
- sizes[it->slabs_clsid]++;
- return;
-}
-
-static void item_unlink_q(item *it) {
- item **head, **tail;
- assert(it->slabs_clsid < LARGEST_ID);
- head = &heads[it->slabs_clsid];
- tail = &tails[it->slabs_clsid];
-
- if (*head == it) {
- assert(it->prev == 0);
- *head = it->next;
- }
- if (*tail == it) {
- assert(it->next == 0);
- *tail = it->prev;
- }
- assert(it->next != it);
- assert(it->prev != it);
-
- if (it->next) it->next->prev = it->prev;
- if (it->prev) it->prev->next = it->next;
- sizes[it->slabs_clsid]--;
- return;
-}
-
-int do_item_link(item *it, const uint32_t hv) {
- MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
- assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
- mutex_lock(&cache_lock);
- it->it_flags |= ITEM_LINKED;
- it->time = current_time;
-
- STATS_LOCK();
- stats.curr_bytes += ITEM_ntotal(it);
- stats.curr_items += 1;
- stats.total_items += 1;
- STATS_UNLOCK();
-
- /* Allocate a new CAS ID on link. */
- ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
- assoc_insert(it, hv);
- item_link_q(it);
- refcount_incr(&it->refcount);
- mutex_unlock(&cache_lock);
-
- return 1;
-}
-
-void do_item_unlink(item *it, const uint32_t hv) {
- MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
- mutex_lock(&cache_lock);
- if ((it->it_flags & ITEM_LINKED) != 0) {
- it->it_flags &= ~ITEM_LINKED;
- STATS_LOCK();
- stats.curr_bytes -= ITEM_ntotal(it);
- stats.curr_items -= 1;
- STATS_UNLOCK();
- assoc_delete(ITEM_key(it), it->nkey, hv);
- item_unlink_q(it);
- do_item_remove(it);
- }
- mutex_unlock(&cache_lock);
-}
-
-/* FIXME: Is it necessary to keep this copy/pasted code? */
-void do_item_unlink_nolock(item *it, const uint32_t hv) {
- MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
- if ((it->it_flags & ITEM_LINKED) != 0) {
- it->it_flags &= ~ITEM_LINKED;
- STATS_LOCK();
- stats.curr_bytes -= ITEM_ntotal(it);
- stats.curr_items -= 1;
- STATS_UNLOCK();
- assoc_delete(ITEM_key(it), it->nkey, hv);
- item_unlink_q(it);
- do_item_remove(it);
- }
-}
-
-void do_item_remove(item *it) {
- MEMCACHED_ITEM_REMOVE(ITEM_key(it), it->nkey, it->nbytes);
- assert((it->it_flags & ITEM_SLABBED) == 0);
-
- if (refcount_decr(&it->refcount) == 0) {
- item_free(it);
- }
-}
-
-void do_item_update(item *it) {
- MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nkey, it->nbytes);
- if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
- assert((it->it_flags & ITEM_SLABBED) == 0);
-
- mutex_lock(&cache_lock);
- if ((it->it_flags & ITEM_LINKED) != 0) {
- item_unlink_q(it);
- it->time = current_time;
- item_link_q(it);
- }
- mutex_unlock(&cache_lock);
- }
-}
-
-int do_item_replace(item *it, item *new_it, const uint32_t hv) {
- MEMCACHED_ITEM_REPLACE(ITEM_key(it), it->nkey, it->nbytes,
- ITEM_key(new_it), new_it->nkey, new_it->nbytes);
- assert((it->it_flags & ITEM_SLABBED) == 0);
-
- do_item_unlink(it, hv);
- return do_item_link(new_it, hv);
-}
-
-/*@null@*/
-char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes) {
- unsigned int memlimit = 2 * 1024 * 1024; /* 2MB max response size */
- char *buffer;
- unsigned int bufcurr;
- item *it;
- unsigned int len;
- unsigned int shown = 0;
- char key_temp[KEY_MAX_LENGTH + 1];
- char temp[512];
-
- it = heads[slabs_clsid];
-
- buffer = malloc((size_t)memlimit);
- if (buffer == 0) return NULL;
- bufcurr = 0;
-
- while (it != NULL && (limit == 0 || shown < limit)) {
- assert(it->nkey <= KEY_MAX_LENGTH);
- /* Copy the key since it may not be null-terminated in the struct */
- strncpy(key_temp, ITEM_key(it), it->nkey);
- key_temp[it->nkey] = 0x00; /* terminate */
- len = snprintf(temp, sizeof(temp), "ITEM %s [%d b; %lu s]\r\n",
- key_temp, it->nbytes - 2,
- (unsigned long)it->exptime + process_started);
- if (bufcurr + len + 6 > memlimit) /* 6 is END\r\n\0 */
- break;
- memcpy(buffer + bufcurr, temp, len);
- bufcurr += len;
- shown++;
- it = it->next;
- }
-
- memcpy(buffer + bufcurr, "END\r\n", 6);
- bufcurr += 5;
-
- *bytes = bufcurr;
- return buffer;
-}
-
-void item_stats_evictions(uint64_t *evicted) {
- int i;
- mutex_lock(&cache_lock);
- for (i = 0; i < LARGEST_ID; i++) {
- evicted[i] = itemstats[i].evicted;
- }
- mutex_unlock(&cache_lock);
-}
-
-void do_item_stats(ADD_STAT add_stats, void *c) {
- int i;
- for (i = 0; i < LARGEST_ID; i++) {
- if (tails[i] != NULL) {
- const char *fmt = "items:%d:%s";
- char key_str[STAT_KEY_LEN];
- char val_str[STAT_VAL_LEN];
- int klen = 0, vlen = 0;
- if (tails[i] == NULL) {
- /* We removed all of the items in this slab class */
- continue;
- }
- APPEND_NUM_FMT_STAT(fmt, i, "number", "%u", sizes[i]);
- APPEND_NUM_FMT_STAT(fmt, i, "age", "%u", current_time - tails[i]->time);
- APPEND_NUM_FMT_STAT(fmt, i, "evicted",
- "%llu", (unsigned long long)itemstats[i].evicted);
- APPEND_NUM_FMT_STAT(fmt, i, "evicted_nonzero",
- "%llu", (unsigned long long)itemstats[i].evicted_nonzero);
- APPEND_NUM_FMT_STAT(fmt, i, "evicted_time",
- "%u", itemstats[i].evicted_time);
- APPEND_NUM_FMT_STAT(fmt, i, "outofmemory",
- "%llu", (unsigned long long)itemstats[i].outofmemory);
- APPEND_NUM_FMT_STAT(fmt, i, "tailrepairs",
- "%llu", (unsigned long long)itemstats[i].tailrepairs);
- APPEND_NUM_FMT_STAT(fmt, i, "reclaimed",
- "%llu", (unsigned long long)itemstats[i].reclaimed);
- APPEND_NUM_FMT_STAT(fmt, i, "expired_unfetched",
- "%llu", (unsigned long long)itemstats[i].expired_unfetched);
- APPEND_NUM_FMT_STAT(fmt, i, "evicted_unfetched",
- "%llu", (unsigned long long)itemstats[i].evicted_unfetched);
- }
- }
-
- /* getting here means both ascii and binary terminators fit */
- add_stats(NULL, 0, NULL, 0, c);
-}
-
-/** dumps out a list of objects of each size, with granularity of 32 bytes */
-/*@null@*/
-void do_item_stats_sizes(ADD_STAT add_stats, void *c) {
-
- /* max 1MB object, divided into 32 bytes size buckets */
- const int num_buckets = 32768;
- unsigned int *histogram = calloc(num_buckets, sizeof(int));
-
- if (histogram != NULL) {
- int i;
-
- /* build the histogram */
- for (i = 0; i < LARGEST_ID; i++) {
- item *iter = heads[i];
- while (iter) {
- int ntotal = ITEM_ntotal(iter);
- int bucket = ntotal / 32;
- if ((ntotal % 32) != 0) bucket++;
- if (bucket < num_buckets) histogram[bucket]++;
- iter = iter->next;
- }
- }
-
- /* write the buffer */
- for (i = 0; i < num_buckets; i++) {
- if (histogram[i] != 0) {
- char key[8];
- snprintf(key, sizeof(key), "%d", i * 32);
- APPEND_STAT(key, "%u", histogram[i]);
- }
- }
- free(histogram);
- }
- add_stats(NULL, 0, NULL, 0, c);
-}
-
-/** wrapper around assoc_find which does the lazy expiration logic */
-item *do_item_get(const char *key, const size_t nkey, const uint32_t hv) {
- mutex_lock(&cache_lock);
- item *it = assoc_find(key, nkey, hv);
- if (it != NULL) {
- refcount_incr(&it->refcount);
- /* Optimization for slab reassignment. prevents popular items from
- * jamming in busy wait. Can only do this here to satisfy lock order
- * of item_lock, cache_lock, slabs_lock. */
- if (slab_rebalance_signal &&
- ((void *)it >= slab_rebal.slab_start && (void *)it < slab_rebal.slab_end)) {
- do_item_unlink_nolock(it, hv);
- do_item_remove(it);
- it = NULL;
- }
- }
- mutex_unlock(&cache_lock);
- int was_found = 0;
-
- if (settings.verbose > 2) {
- if (it == NULL) {
- fprintf(stderr, "> NOT FOUND %s", key);
- } else {
- fprintf(stderr, "> FOUND KEY %s", ITEM_key(it));
- was_found++;
- }
- }
-
- if (it != NULL) {
- if (settings.oldest_live != 0 && settings.oldest_live <= current_time &&
- it->time <= settings.oldest_live) {
- do_item_unlink(it, hv);
- do_item_remove(it);
- it = NULL;
- if (was_found) {
- fprintf(stderr, " -nuked by flush");
- }
- } else if (it->exptime != 0 && it->exptime <= current_time) {
- do_item_unlink(it, hv);
- do_item_remove(it);
- it = NULL;
- if (was_found) {
- fprintf(stderr, " -nuked by expire");
- }
- } else {
- it->it_flags |= ITEM_FETCHED;
- DEBUG_REFCNT(it, '+');
- }
- }
-
- if (settings.verbose > 2)
- fprintf(stderr, "\n");
-
- return it;
-}
-
-item *do_item_touch(const char *key, size_t nkey, uint32_t exptime,
- const uint32_t hv) {
- item *it = do_item_get(key, nkey, hv);
- if (it != NULL) {
- it->exptime = exptime;
- }
- return it;
-}
-
-/* expires items that are more recent than the oldest_live setting. */
-void do_item_flush_expired(void) {
- int i;
- item *iter, *next;
- if (settings.oldest_live == 0)
- return;
- for (i = 0; i < LARGEST_ID; i++) {
- /* The LRU is sorted in decreasing time order, and an item's timestamp
- * is never newer than its last access time, so we only need to walk
- * back until we hit an item older than the oldest_live time.
- * The oldest_live checking will auto-expire the remaining items.
- */
- for (iter = heads[i]; iter != NULL; iter = next) {
- if (iter->time >= settings.oldest_live) {
- next = iter->next;
- if ((iter->it_flags & ITEM_SLABBED) == 0) {
- do_item_unlink_nolock(iter, hash(ITEM_key(iter), iter->nkey, 0));
- }
- } else {
- /* We've hit the first old item. Continue to the next queue. */
- break;
- }
- }
- }
-}
+++ /dev/null
-/* See items.c */
-uint64_t get_cas_id(void);
-
-/*@null@*/
-item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes);
-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 */
-int do_item_replace(item *it, item *new_it, const uint32_t hv);
-
-/*@null@*/
-char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
-void do_item_stats(ADD_STAT add_stats, void *c);
-/*@null@*/
-void do_item_stats_sizes(ADD_STAT add_stats, void *c);
-void do_item_flush_expired(void);
-
-item *do_item_get(const char *key, const size_t nkey, const uint32_t hv);
-item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv);
-void item_stats_reset(void);
-extern pthread_mutex_t cache_lock;
-void item_stats_evictions(uint64_t *evicted);
+++ /dev/null
-# 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 <stdarg.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <wchar.h>
-#include <stdio.h>
-
-// 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
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- * memcached - memory caching daemon
- *
- * http://www.danga.com/memcached/
- *
- * Copyright 2003 Danga Interactive, Inc. All rights reserved.
- *
- * Use and distribution licensed under the BSD license. See
- * the LICENSE file for full text.
- *
- * Authors:
- * Anatoly Vorobey <mellon@pobox.com>
- * Brad Fitzpatrick <brad@danga.com>
- */
-#include "memcached.h"
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <signal.h>
-#include <sys/resource.h>
-#include <sys/uio.h>
-#include <ctype.h>
-#include <stdarg.h>
-
-/* some POSIX systems need the following definition
- * to get mlockall flags out of sys/mman.h. */
-#ifndef _P1003_1B_VISIBLE
-#define _P1003_1B_VISIBLE
-#endif
-/* need this to get IOV_MAX on some platforms. */
-#ifndef __need_IOV_MAX
-#define __need_IOV_MAX
-#endif
-#include <pwd.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <assert.h>
-#include <limits.h>
-#include <sysexits.h>
-#include <stddef.h>
-
-/* FreeBSD 4.x doesn't have IOV_MAX exposed. */
-#ifndef IOV_MAX
-#if defined(__FreeBSD__) || defined(__APPLE__)
-# define IOV_MAX 1024
-#endif
-#endif
-
-/*
- * forward declarations
- */
-static void drive_machine(conn *c);
-static int new_socket(struct addrinfo *ai);
-static int try_read_command(conn *c);
-
-enum try_read_result {
- READ_DATA_RECEIVED,
- READ_NO_DATA_RECEIVED,
- READ_ERROR, /** an error occured (on the socket) (or client closed connection) */
- READ_MEMORY_ERROR /** failed to allocate more memory */
-};
-
-static enum try_read_result try_read_network(conn *c);
-static enum try_read_result try_read_udp(conn *c);
-
-static void conn_set_state(conn *c, enum conn_states state);
-
-/* stats */
-static void stats_init(void);
-static void server_stats(ADD_STAT add_stats, conn *c);
-static void process_stat_settings(ADD_STAT add_stats, void *c);
-
-
-/* defaults */
-static void settings_init(void);
-
-/* event handling, network IO */
-static void event_handler(const int fd, const short which, void *arg);
-static void conn_close(conn *c);
-static void conn_init(void);
-static bool update_event(conn *c, const int new_flags);
-static void complete_nread(conn *c);
-static void process_command(conn *c, char *command);
-static void write_and_free(conn *c, char *buf, int bytes);
-static int ensure_iov_space(conn *c);
-static int add_iov(conn *c, const void *buf, int len);
-static int add_msghdr(conn *c);
-
-
-static void conn_free(conn *c);
-
-/** exported globals **/
-struct stats stats;
-struct settings settings;
-time_t process_started; /* when the process was started */
-
-struct slab_rebalance slab_rebal;
-volatile int slab_rebalance_signal;
-
-/** file scope variables **/
-static conn *listen_conn = NULL;
-static struct event_base *main_base;
-
-enum transmit_result {
- TRANSMIT_COMPLETE, /** All done writing. */
- TRANSMIT_INCOMPLETE, /** More data remaining to write. */
- TRANSMIT_SOFT_ERROR, /** Can't write any more right now. */
- TRANSMIT_HARD_ERROR /** Can't write (c->state is set to conn_closing) */
-};
-
-static enum transmit_result transmit(conn *c);
-
-/* This reduces the latency without adding lots of extra wiring to be able to
- * notify the listener thread of when to listen again.
- * Also, the clock timer could be broken out into its own thread and we
- * can block the listener via a condition.
- */
-static volatile bool allow_new_conns = true;
-static struct event maxconnsevent;
-static void maxconns_handler(const int fd, const short which, void *arg) {
- struct timeval t = {.tv_sec = 0, .tv_usec = 10000};
-
- if (fd == -42 || allow_new_conns == false) {
- /* reschedule in 10ms if we need to keep polling */
- evtimer_set(&maxconnsevent, maxconns_handler, 0);
- event_base_set(main_base, &maxconnsevent);
- evtimer_add(&maxconnsevent, &t);
- } else {
- evtimer_del(&maxconnsevent);
- accept_new_conns(true);
- }
-}
-
-#define REALTIME_MAXDELTA 60*60*24*30
-
-/*
- * given time value that's either unix time or delta from current unix time, return
- * unix time. Use the fact that delta can't exceed one month (and real time value can't
- * be that low).
- */
-static rel_time_t realtime(const time_t exptime) {
- /* no. of seconds in 30 days - largest possible delta exptime */
-
- if (exptime == 0) return 0; /* 0 means never expire */
-
- if (exptime > REALTIME_MAXDELTA) {
- /* if item expiration is at/before the server started, give it an
- expiration time of 1 second after the server started.
- (because 0 means don't expire). without this, we'd
- underflow and wrap around to some large value way in the
- future, effectively making items expiring in the past
- really expiring never */
- if (exptime <= process_started)
- return (rel_time_t)1;
- return (rel_time_t)(exptime - process_started);
- } else {
- return (rel_time_t)(exptime + current_time);
- }
-}
-
-static void stats_init(void) {
- stats.curr_items = stats.total_items = stats.curr_conns = stats.total_conns = stats.conn_structs = 0;
- stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = stats.reclaimed = 0;
- stats.touch_cmds = stats.touch_misses = stats.touch_hits = stats.rejected_conns = 0;
- stats.curr_bytes = stats.listen_disabled_num = 0;
- stats.hash_power_level = stats.hash_bytes = stats.hash_is_expanding = 0;
- stats.expired_unfetched = stats.evicted_unfetched = 0;
- stats.slabs_moved = 0;
- stats.accepting_conns = true; /* assuming we start in this state. */
- stats.slab_reassign_running = false;
-
- /* make the time we started always be 2 seconds before we really
- did, so time(0) - time.started is never zero. if so, things
- like 'settings.oldest_live' which act as booleans as well as
- values are now false in boolean context... */
- process_started = time(0) - 2;
- stats_prefix_init();
-}
-
-static void stats_reset(void) {
- STATS_LOCK();
- stats.total_items = stats.total_conns = 0;
- stats.rejected_conns = 0;
- stats.evictions = 0;
- stats.reclaimed = 0;
- stats.listen_disabled_num = 0;
- stats_prefix_clear();
- STATS_UNLOCK();
- threadlocal_stats_reset();
- item_stats_reset();
-}
-
-static void settings_init(void) {
- settings.use_cas = true;
- settings.access = 0700;
- settings.port = 11211;
- settings.udpport = 11211;
- /* By default this string should be NULL for getaddrinfo() */
- settings.inter = NULL;
- settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */
- settings.maxconns = 1024; /* to limit connections-related memory to about 5MB */
- settings.verbose = 0;
- settings.oldest_live = 0;
- settings.evict_to_free = 1; /* push old items out of cache when memory runs out */
- settings.socketpath = NULL; /* by default, not using a unix socket */
- settings.factor = 1.25;
- settings.chunk_size = 48; /* space for a modest key and value */
- settings.num_threads = 4; /* N workers */
- settings.num_threads_per_udp = 0;
- settings.prefix_delimiter = ':';
- settings.detail_enabled = 0;
- settings.reqs_per_event = 20;
- settings.backlog = 1024;
- settings.binding_protocol = negotiating_prot;
- settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */
- settings.maxconns_fast = false;
- settings.hashpower_init = 0;
- settings.slab_reassign = false;
- settings.slab_automove = 0;
-}
-
-/*
- * Adds a message header to a connection.
- *
- * Returns 0 on success, -1 on out-of-memory.
- */
-static int add_msghdr(conn *c)
-{
- struct msghdr *msg;
-
- assert(c != NULL);
-
- if (c->msgsize == c->msgused) {
- msg = realloc(c->msglist, c->msgsize * 2 * sizeof(struct msghdr));
- if (! msg)
- return -1;
- c->msglist = msg;
- c->msgsize *= 2;
- }
-
- msg = c->msglist + c->msgused;
-
- /* this wipes msg_iovlen, msg_control, msg_controllen, and
- msg_flags, the last 3 of which aren't defined on solaris: */
- memset(msg, 0, sizeof(struct msghdr));
-
- msg->msg_iov = &c->iov[c->iovused];
-
- if (c->request_addr_size > 0) {
- msg->msg_name = &c->request_addr;
- msg->msg_namelen = c->request_addr_size;
- }
-
- c->msgbytes = 0;
- c->msgused++;
-
- if (IS_UDP(c->transport)) {
- /* Leave room for the UDP header, which we'll fill in later. */
- return add_iov(c, NULL, UDP_HEADER_SIZE);
- }
-
- return 0;
-}
-
-
-/*
- * Free list management for connections.
- */
-
-static conn **freeconns;
-static int freetotal;
-static int freecurr;
-/* Lock for connection freelist */
-static pthread_mutex_t conn_lock = PTHREAD_MUTEX_INITIALIZER;
-
-
-static void conn_init(void) {
- freetotal = 200;
- freecurr = 0;
- if ((freeconns = calloc(freetotal, sizeof(conn *))) == NULL) {
- fprintf(stderr, "Failed to allocate connection structures\n");
- }
- return;
-}
-
-/*
- * Returns a connection from the freelist, if any.
- */
-conn *conn_from_freelist() {
- conn *c;
-
- pthread_mutex_lock(&conn_lock);
- if (freecurr > 0) {
- c = freeconns[--freecurr];
- } else {
- c = NULL;
- }
- pthread_mutex_unlock(&conn_lock);
-
- return c;
-}
-
-/*
- * Adds a connection to the freelist. 0 = success.
- */
-bool conn_add_to_freelist(conn *c) {
- bool ret = true;
- pthread_mutex_lock(&conn_lock);
- if (freecurr < freetotal) {
- freeconns[freecurr++] = c;
- ret = false;
- } else {
- /* try to enlarge free connections array */
- size_t newsize = freetotal * 2;
- conn **new_freeconns = realloc(freeconns, sizeof(conn *) * newsize);
- if (new_freeconns) {
- freetotal = newsize;
- freeconns = new_freeconns;
- freeconns[freecurr++] = c;
- ret = false;
- }
- }
- pthread_mutex_unlock(&conn_lock);
- return ret;
-}
-
-static const char *prot_text(enum protocol prot) {
- char *rv = "unknown";
- switch(prot) {
- case ascii_prot:
- rv = "ascii";
- break;
- case binary_prot:
- rv = "binary";
- break;
- case negotiating_prot:
- rv = "auto-negotiate";
- break;
- }
- return rv;
-}
-
-conn *conn_new(const int sfd, enum conn_states init_state,
- const int event_flags,
- const int read_buffer_size, enum network_transport transport,
- struct event_base *base) {
- conn *c = conn_from_freelist();
-
- if (NULL == c) {
- if (!(c = (conn *)calloc(1, sizeof(conn)))) {
- fprintf(stderr, "calloc()\n");
- return NULL;
- }
- MEMCACHED_CONN_CREATE(c);
-
- c->rbuf = c->wbuf = 0;
- c->ilist = 0;
- c->suffixlist = 0;
- c->iov = 0;
- c->msglist = 0;
- c->hdrbuf = 0;
-
- c->rsize = read_buffer_size;
- c->wsize = DATA_BUFFER_SIZE;
- c->isize = ITEM_LIST_INITIAL;
- c->suffixsize = SUFFIX_LIST_INITIAL;
- c->iovsize = IOV_LIST_INITIAL;
- c->msgsize = MSG_LIST_INITIAL;
- c->hdrsize = 0;
-
- c->rbuf = (char *)malloc((size_t)c->rsize);
- c->wbuf = (char *)malloc((size_t)c->wsize);
- c->ilist = (item **)malloc(sizeof(item *) * c->isize);
- c->suffixlist = (char **)malloc(sizeof(char *) * c->suffixsize);
- c->iov = (struct iovec *)malloc(sizeof(struct iovec) * c->iovsize);
- c->msglist = (struct msghdr *)malloc(sizeof(struct msghdr) * c->msgsize);
-
- if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0 || c->iov == 0 ||
- c->msglist == 0 || c->suffixlist == 0) {
- conn_free(c);
- fprintf(stderr, "malloc()\n");
- return NULL;
- }
-
- STATS_LOCK();
- stats.conn_structs++;
- STATS_UNLOCK();
- }
-
- c->transport = transport;
- c->protocol = settings.binding_protocol;
-
- /* unix socket mode doesn't need this, so zeroed out. but why
- * is this done for every command? presumably for UDP
- * mode. */
- if (!settings.socketpath) {
- c->request_addr_size = sizeof(c->request_addr);
- } else {
- c->request_addr_size = 0;
- }
-
- if (settings.verbose > 1) {
- if (init_state == conn_listening) {
- fprintf(stderr, "<%d server listening (%s)\n", sfd,
- prot_text(c->protocol));
- } else if (IS_UDP(transport)) {
- fprintf(stderr, "<%d server listening (udp)\n", sfd);
- } else if (c->protocol == negotiating_prot) {
- fprintf(stderr, "<%d new auto-negotiating client connection\n",
- sfd);
- } else if (c->protocol == ascii_prot) {
- fprintf(stderr, "<%d new ascii client connection.\n", sfd);
- } else if (c->protocol == binary_prot) {
- fprintf(stderr, "<%d new binary client connection.\n", sfd);
- } else {
- fprintf(stderr, "<%d new unknown (%d) client connection\n",
- sfd, c->protocol);
- assert(false);
- }
- }
-
- c->sfd = sfd;
- c->state = init_state;
- c->rlbytes = 0;
- c->cmd = -1;
- c->rbytes = c->wbytes = 0;
- c->wcurr = c->wbuf;
- c->rcurr = c->rbuf;
- c->ritem = 0;
- c->icurr = c->ilist;
- c->suffixcurr = c->suffixlist;
- c->ileft = 0;
- c->suffixleft = 0;
- c->iovused = 0;
- c->msgcurr = 0;
- c->msgused = 0;
-
- c->write_and_go = init_state;
- c->write_and_free = 0;
- c->item = 0;
-
- c->noreply = false;
-
- event_set(&c->event, sfd, event_flags, event_handler, (void *)c);
- event_base_set(base, &c->event);
- c->ev_flags = event_flags;
-
- if (event_add(&c->event, 0) == -1) {
- if (conn_add_to_freelist(c)) {
- conn_free(c);
- }
- perror("event_add");
- return NULL;
- }
-
- STATS_LOCK();
- stats.curr_conns++;
- stats.total_conns++;
- STATS_UNLOCK();
-
- MEMCACHED_CONN_ALLOCATE(c->sfd);
-
- return c;
-}
-
-static void conn_cleanup(conn *c) {
- assert(c != NULL);
-
- if (c->item) {
- item_remove(c->item);
- c->item = 0;
- }
-
- if (c->ileft != 0) {
- for (; c->ileft > 0; c->ileft--,c->icurr++) {
- item_remove(*(c->icurr));
- }
- }
-
- if (c->suffixleft != 0) {
- for (; c->suffixleft > 0; c->suffixleft--, c->suffixcurr++) {
- cache_free(c->thread->suffix_cache, *(c->suffixcurr));
- }
- }
-
- if (c->write_and_free) {
- free(c->write_and_free);
- c->write_and_free = 0;
- }
-
- if (c->sasl_conn) {
- assert(settings.sasl);
- sasl_dispose(&c->sasl_conn);
- c->sasl_conn = NULL;
- }
-
- if (IS_UDP(c->transport)) {
- conn_set_state(c, conn_read);
- }
-}
-
-/*
- * Frees a connection.
- */
-void conn_free(conn *c) {
- if (c) {
- MEMCACHED_CONN_DESTROY(c);
- if (c->hdrbuf)
- free(c->hdrbuf);
- if (c->msglist)
- free(c->msglist);
- if (c->rbuf)
- free(c->rbuf);
- if (c->wbuf)
- free(c->wbuf);
- if (c->ilist)
- free(c->ilist);
- if (c->suffixlist)
- free(c->suffixlist);
- if (c->iov)
- free(c->iov);
- free(c);
- }
-}
-
-static void conn_close(conn *c) {
- assert(c != NULL);
-
- /* delete the event, the socket and the conn */
- event_del(&c->event);
-
- if (settings.verbose > 1)
- fprintf(stderr, "<%d connection closed.\n", c->sfd);
-
- MEMCACHED_CONN_RELEASE(c->sfd);
- close(c->sfd);
- pthread_mutex_lock(&conn_lock);
- allow_new_conns = true;
- pthread_mutex_unlock(&conn_lock);
- conn_cleanup(c);
-
- /* if the connection has big buffers, just free it */
- if (c->rsize > READ_BUFFER_HIGHWAT || conn_add_to_freelist(c)) {
- conn_free(c);
- }
-
- STATS_LOCK();
- stats.curr_conns--;
- STATS_UNLOCK();
-
- return;
-}
-
-/*
- * Shrinks a connection's buffers if they're too big. This prevents
- * periodic large "get" requests from permanently chewing lots of server
- * memory.
- *
- * This should only be called in between requests since it can wipe output
- * buffers!
- */
-static void conn_shrink(conn *c) {
- assert(c != NULL);
-
- if (IS_UDP(c->transport))
- return;
-
- if (c->rsize > READ_BUFFER_HIGHWAT && c->rbytes < DATA_BUFFER_SIZE) {
- char *newbuf;
-
- if (c->rcurr != c->rbuf)
- memmove(c->rbuf, c->rcurr, (size_t)c->rbytes);
-
- newbuf = (char *)realloc((void *)c->rbuf, DATA_BUFFER_SIZE);
-
- if (newbuf) {
- c->rbuf = newbuf;
- c->rsize = DATA_BUFFER_SIZE;
- }
- /* TODO check other branch... */
- c->rcurr = c->rbuf;
- }
-
- if (c->isize > ITEM_LIST_HIGHWAT) {
- item **newbuf = (item**) realloc((void *)c->ilist, ITEM_LIST_INITIAL * sizeof(c->ilist[0]));
- if (newbuf) {
- c->ilist = newbuf;
- c->isize = ITEM_LIST_INITIAL;
- }
- /* TODO check error condition? */
- }
-
- if (c->msgsize > MSG_LIST_HIGHWAT) {
- struct msghdr *newbuf = (struct msghdr *) realloc((void *)c->msglist, MSG_LIST_INITIAL * sizeof(c->msglist[0]));
- if (newbuf) {
- c->msglist = newbuf;
- c->msgsize = MSG_LIST_INITIAL;
- }
- /* TODO check error condition? */
- }
-
- if (c->iovsize > IOV_LIST_HIGHWAT) {
- struct iovec *newbuf = (struct iovec *) realloc((void *)c->iov, IOV_LIST_INITIAL * sizeof(c->iov[0]));
- if (newbuf) {
- c->iov = newbuf;
- c->iovsize = IOV_LIST_INITIAL;
- }
- /* TODO check return value */
- }
-}
-
-/**
- * Convert a state name to a human readable form.
- */
-static const char *state_text(enum conn_states state) {
- const char* const statenames[] = { "conn_listening",
- "conn_new_cmd",
- "conn_waiting",
- "conn_read",
- "conn_parse_cmd",
- "conn_write",
- "conn_nread",
- "conn_swallow",
- "conn_closing",
- "conn_mwrite" };
- return statenames[state];
-}
-
-/*
- * Sets a connection's current state in the state machine. Any special
- * processing that needs to happen on certain state transitions can
- * happen here.
- */
-static void conn_set_state(conn *c, enum conn_states state) {
- assert(c != NULL);
- assert(state >= conn_listening && state < conn_max_state);
-
- if (state != c->state) {
- if (settings.verbose > 2) {
- fprintf(stderr, "%d: going from %s to %s\n",
- c->sfd, state_text(c->state),
- state_text(state));
- }
-
- if (state == conn_write || state == conn_mwrite) {
- MEMCACHED_PROCESS_COMMAND_END(c->sfd, c->wbuf, c->wbytes);
- }
- c->state = state;
- }
-}
-
-/*
- * Ensures that there is room for another struct iovec in a connection's
- * iov list.
- *
- * Returns 0 on success, -1 on out-of-memory.
- */
-static int ensure_iov_space(conn *c) {
- assert(c != NULL);
-
- if (c->iovused >= c->iovsize) {
- int i, iovnum;
- struct iovec *new_iov = (struct iovec *)realloc(c->iov,
- (c->iovsize * 2) * sizeof(struct iovec));
- if (! new_iov)
- return -1;
- c->iov = new_iov;
- c->iovsize *= 2;
-
- /* Point all the msghdr structures at the new list. */
- for (i = 0, iovnum = 0; i < c->msgused; i++) {
- c->msglist[i].msg_iov = &c->iov[iovnum];
- iovnum += c->msglist[i].msg_iovlen;
- }
- }
-
- return 0;
-}
-
-
-/*
- * Adds data to the list of pending data that will be written out to a
- * connection.
- *
- * Returns 0 on success, -1 on out-of-memory.
- */
-
-static int add_iov(conn *c, const void *buf, int len) {
- struct msghdr *m;
- int leftover;
- bool limit_to_mtu;
-
- assert(c != NULL);
-
- do {
- m = &c->msglist[c->msgused - 1];
-
- /*
- * Limit UDP packets, and the first payloads of TCP replies, to
- * UDP_MAX_PAYLOAD_SIZE bytes.
- */
- limit_to_mtu = IS_UDP(c->transport) || (1 == c->msgused);
-
- /* We may need to start a new msghdr if this one is full. */
- if (m->msg_iovlen == IOV_MAX ||
- (limit_to_mtu && c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) {
- add_msghdr(c);
- m = &c->msglist[c->msgused - 1];
- }
-
- if (ensure_iov_space(c) != 0)
- return -1;
-
- /* If the fragment is too big to fit in the datagram, split it up */
- if (limit_to_mtu && len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) {
- leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE;
- len -= leftover;
- } else {
- leftover = 0;
- }
-
- m = &c->msglist[c->msgused - 1];
- m->msg_iov[m->msg_iovlen].iov_base = (void *)buf;
- m->msg_iov[m->msg_iovlen].iov_len = len;
-
- c->msgbytes += len;
- c->iovused++;
- m->msg_iovlen++;
-
- buf = ((char *)buf) + len;
- len = leftover;
- } while (leftover > 0);
-
- return 0;
-}
-
-
-/*
- * Constructs a set of UDP headers and attaches them to the outgoing messages.
- */
-static int build_udp_headers(conn *c) {
- int i;
- unsigned char *hdr;
-
- assert(c != NULL);
-
- if (c->msgused > c->hdrsize) {
- void *new_hdrbuf;
- if (c->hdrbuf)
- new_hdrbuf = realloc(c->hdrbuf, c->msgused * 2 * UDP_HEADER_SIZE);
- else
- new_hdrbuf = malloc(c->msgused * 2 * UDP_HEADER_SIZE);
- if (! new_hdrbuf)
- return -1;
- c->hdrbuf = (unsigned char *)new_hdrbuf;
- c->hdrsize = c->msgused * 2;
- }
-
- hdr = c->hdrbuf;
- for (i = 0; i < c->msgused; i++) {
- c->msglist[i].msg_iov[0].iov_base = (void*)hdr;
- c->msglist[i].msg_iov[0].iov_len = UDP_HEADER_SIZE;
- *hdr++ = c->request_id / 256;
- *hdr++ = c->request_id % 256;
- *hdr++ = i / 256;
- *hdr++ = i % 256;
- *hdr++ = c->msgused / 256;
- *hdr++ = c->msgused % 256;
- *hdr++ = 0;
- *hdr++ = 0;
- assert((void *) hdr == (caddr_t)c->msglist[i].msg_iov[0].iov_base + UDP_HEADER_SIZE);
- }
-
- return 0;
-}
-
-
-static void out_string(conn *c, const char *str) {
- size_t len;
-
- assert(c != NULL);
-
- if (c->noreply) {
- if (settings.verbose > 1)
- fprintf(stderr, ">%d NOREPLY %s\n", c->sfd, str);
- c->noreply = false;
- conn_set_state(c, conn_new_cmd);
- return;
- }
-
- if (settings.verbose > 1)
- fprintf(stderr, ">%d %s\n", c->sfd, str);
-
- /* Nuke a partial output... */
- c->msgcurr = 0;
- c->msgused = 0;
- c->iovused = 0;
- add_msghdr(c);
-
- len = strlen(str);
- if ((len + 2) > c->wsize) {
- /* ought to be always enough. just fail for simplicity */
- str = "SERVER_ERROR output line too long";
- len = strlen(str);
- }
-
- memcpy(c->wbuf, str, len);
- memcpy(c->wbuf + len, "\r\n", 2);
- c->wbytes = len + 2;
- c->wcurr = c->wbuf;
-
- conn_set_state(c, conn_write);
- c->write_and_go = conn_new_cmd;
- return;
-}
-
-/*
- * we get here after reading the value in set/add/replace commands. The command
- * has been stored in c->cmd, and the item is ready in c->item.
- */
-static void complete_nread_ascii(conn *c) {
- assert(c != NULL);
-
- item *it = c->item;
- int comm = c->cmd;
- enum store_item_type ret;
-
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[it->slabs_clsid].set_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) {
- out_string(c, "CLIENT_ERROR bad data chunk");
- } else {
- ret = store_item(it, comm, c);
-
-#ifdef ENABLE_DTRACE
- uint64_t cas = ITEM_get_cas(it);
- switch (c->cmd) {
- case NREAD_ADD:
- MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
- (ret == 1) ? it->nbytes : -1, cas);
- break;
- case NREAD_REPLACE:
- MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
- (ret == 1) ? it->nbytes : -1, cas);
- break;
- case NREAD_APPEND:
- MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
- (ret == 1) ? it->nbytes : -1, cas);
- break;
- case NREAD_PREPEND:
- MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
- (ret == 1) ? it->nbytes : -1, cas);
- break;
- case NREAD_SET:
- MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
- (ret == 1) ? it->nbytes : -1, cas);
- break;
- case NREAD_CAS:
- MEMCACHED_COMMAND_CAS(c->sfd, ITEM_key(it), it->nkey, it->nbytes,
- cas);
- break;
- }
-#endif
-
- switch (ret) {
- case STORED:
- out_string(c, "STORED");
- break;
- case EXISTS:
- out_string(c, "EXISTS");
- break;
- case NOT_FOUND:
- out_string(c, "NOT_FOUND");
- break;
- case NOT_STORED:
- out_string(c, "NOT_STORED");
- break;
- default:
- out_string(c, "SERVER_ERROR Unhandled storage type.");
- }
-
- }
-
- item_remove(c->item); /* release the c->item reference */
- c->item = 0;
-}
-
-/**
- * get a pointer to the start of the request struct for the current command
- */
-static void* binary_get_request(conn *c) {
- char *ret = c->rcurr;
- ret -= (sizeof(c->binary_header) + c->binary_header.request.keylen +
- c->binary_header.request.extlen);
-
- assert(ret >= c->rbuf);
- return ret;
-}
-
-/**
- * get a pointer to the key in this request
- */
-static char* binary_get_key(conn *c) {
- return c->rcurr - (c->binary_header.request.keylen);
-}
-
-static void add_bin_header(conn *c, uint16_t err, uint8_t hdr_len, uint16_t key_len, uint32_t body_len) {
- protocol_binary_response_header* header;
-
- assert(c);
-
- c->msgcurr = 0;
- c->msgused = 0;
- c->iovused = 0;
- if (add_msghdr(c) != 0) {
- /* XXX: out_string is inappropriate here */
- out_string(c, "SERVER_ERROR out of memory");
- return;
- }
-
- header = (protocol_binary_response_header *)c->wbuf;
-
- header->response.magic = (uint8_t)PROTOCOL_BINARY_RES;
- header->response.opcode = c->binary_header.request.opcode;
- header->response.keylen = (uint16_t)htons(key_len);
-
- header->response.extlen = (uint8_t)hdr_len;
- header->response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES;
- header->response.status = (uint16_t)htons(err);
-
- header->response.bodylen = htonl(body_len);
- header->response.opaque = c->opaque;
- header->response.cas = htonll(c->cas);
-
- if (settings.verbose > 1) {
- int ii;
- fprintf(stderr, ">%d Writing bin response:", c->sfd);
- for (ii = 0; ii < sizeof(header->bytes); ++ii) {
- if (ii % 4 == 0) {
- fprintf(stderr, "\n>%d ", c->sfd);
- }
- fprintf(stderr, " 0x%02x", header->bytes[ii]);
- }
- fprintf(stderr, "\n");
- }
-
- add_iov(c, c->wbuf, sizeof(header->response));
-}
-
-static void write_bin_error(conn *c, protocol_binary_response_status err, int swallow) {
- const char *errstr = "Unknown error";
- size_t len;
-
- switch (err) {
- case PROTOCOL_BINARY_RESPONSE_ENOMEM:
- errstr = "Out of memory";
- break;
- case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
- errstr = "Unknown command";
- break;
- case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
- errstr = "Not found";
- break;
- case PROTOCOL_BINARY_RESPONSE_EINVAL:
- errstr = "Invalid arguments";
- break;
- case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
- errstr = "Data exists for key.";
- break;
- case PROTOCOL_BINARY_RESPONSE_E2BIG:
- errstr = "Too large.";
- break;
- case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL:
- errstr = "Non-numeric server-side value for incr or decr";
- break;
- case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
- errstr = "Not stored.";
- break;
- case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
- errstr = "Auth failure.";
- break;
- default:
- assert(false);
- errstr = "UNHANDLED ERROR";
- fprintf(stderr, ">%d UNHANDLED ERROR: %d\n", c->sfd, err);
- }
-
- if (settings.verbose > 1) {
- fprintf(stderr, ">%d Writing an error: %s\n", c->sfd, errstr);
- }
-
- len = strlen(errstr);
- add_bin_header(c, err, 0, 0, len);
- if (len > 0) {
- add_iov(c, errstr, len);
- }
- conn_set_state(c, conn_mwrite);
- if(swallow > 0) {
- c->sbytes = swallow;
- c->write_and_go = conn_swallow;
- } else {
- c->write_and_go = conn_new_cmd;
- }
-}
-
-/* Form and send a response to a command over the binary protocol */
-static void write_bin_response(conn *c, void *d, int hlen, int keylen, int dlen) {
- if (!c->noreply || c->cmd == PROTOCOL_BINARY_CMD_GET ||
- c->cmd == PROTOCOL_BINARY_CMD_GETK) {
- add_bin_header(c, 0, hlen, keylen, dlen);
- if(dlen > 0) {
- add_iov(c, d, dlen);
- }
- conn_set_state(c, conn_mwrite);
- c->write_and_go = conn_new_cmd;
- } else {
- conn_set_state(c, conn_new_cmd);
- }
-}
-
-static void complete_incr_bin(conn *c) {
- item *it;
- char *key;
- size_t nkey;
- /* Weird magic in add_delta forces me to pad here */
- char tmpbuf[INCR_MAX_STORAGE_LEN];
- uint64_t cas = 0;
-
- protocol_binary_response_incr* rsp = (protocol_binary_response_incr*)c->wbuf;
- protocol_binary_request_incr* req = binary_get_request(c);
-
- assert(c != NULL);
- assert(c->wsize >= sizeof(*rsp));
-
- /* fix byteorder in the request */
- req->message.body.delta = ntohll(req->message.body.delta);
- req->message.body.initial = ntohll(req->message.body.initial);
- req->message.body.expiration = ntohl(req->message.body.expiration);
- key = binary_get_key(c);
- nkey = c->binary_header.request.keylen;
-
- if (settings.verbose > 1) {
- int i;
- fprintf(stderr, "incr ");
-
- for (i = 0; i < nkey; i++) {
- fprintf(stderr, "%c", key[i]);
- }
- fprintf(stderr, " %lld, %llu, %d\n",
- (long long)req->message.body.delta,
- (long long)req->message.body.initial,
- req->message.body.expiration);
- }
-
- if (c->binary_header.request.cas != 0) {
- cas = c->binary_header.request.cas;
- }
- switch(add_delta(c, key, nkey, c->cmd == PROTOCOL_BINARY_CMD_INCREMENT,
- req->message.body.delta, tmpbuf,
- &cas)) {
- case OK:
- rsp->message.body.value = htonll(strtoull(tmpbuf, NULL, 10));
- if (cas) {
- c->cas = cas;
- }
- write_bin_response(c, &rsp->message.body, 0, 0,
- sizeof(rsp->message.body.value));
- break;
- case NON_NUMERIC:
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL, 0);
- break;
- case EOM:
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, 0);
- break;
- case DELTA_ITEM_NOT_FOUND:
- if (req->message.body.expiration != 0xffffffff) {
- /* Save some room for the response */
- rsp->message.body.value = htonll(req->message.body.initial);
- it = item_alloc(key, nkey, 0, realtime(req->message.body.expiration),
- INCR_MAX_STORAGE_LEN);
-
- if (it != NULL) {
- snprintf(ITEM_data(it), INCR_MAX_STORAGE_LEN, "%llu",
- (unsigned long long)req->message.body.initial);
-
- if (store_item(it, NREAD_ADD, c)) {
- c->cas = ITEM_get_cas(it);
- write_bin_response(c, &rsp->message.body, 0, 0, sizeof(rsp->message.body.value));
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_NOT_STORED, 0);
- }
- item_remove(it); /* release our reference */
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, 0);
- }
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- if (c->cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
- c->thread->stats.incr_misses++;
- } else {
- c->thread->stats.decr_misses++;
- }
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- }
- break;
- case DELTA_ITEM_CAS_MISMATCH:
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, 0);
- break;
- }
-}
-
-static void complete_update_bin(conn *c) {
- protocol_binary_response_status eno = PROTOCOL_BINARY_RESPONSE_EINVAL;
- enum store_item_type ret = NOT_STORED;
- assert(c != NULL);
-
- item *it = c->item;
-
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[it->slabs_clsid].set_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- /* We don't actually receive the trailing two characters in the bin
- * protocol, so we're going to just set them here */
- *(ITEM_data(it) + it->nbytes - 2) = '\r';
- *(ITEM_data(it) + it->nbytes - 1) = '\n';
-
- ret = store_item(it, c->cmd, c);
-
-#ifdef ENABLE_DTRACE
- uint64_t cas = ITEM_get_cas(it);
- switch (c->cmd) {
- case NREAD_ADD:
- MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
- (ret == STORED) ? it->nbytes : -1, cas);
- break;
- case NREAD_REPLACE:
- MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
- (ret == STORED) ? it->nbytes : -1, cas);
- break;
- case NREAD_APPEND:
- MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
- (ret == STORED) ? it->nbytes : -1, cas);
- break;
- case NREAD_PREPEND:
- MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
- (ret == STORED) ? it->nbytes : -1, cas);
- break;
- case NREAD_SET:
- MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
- (ret == STORED) ? it->nbytes : -1, cas);
- break;
- }
-#endif
-
- switch (ret) {
- case STORED:
- /* Stored */
- write_bin_response(c, NULL, 0, 0, 0);
- break;
- case EXISTS:
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, 0);
- break;
- case NOT_FOUND:
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- break;
- case NOT_STORED:
- if (c->cmd == NREAD_ADD) {
- eno = PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
- } else if(c->cmd == NREAD_REPLACE) {
- eno = PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
- } else {
- eno = PROTOCOL_BINARY_RESPONSE_NOT_STORED;
- }
- write_bin_error(c, eno, 0);
- }
-
- item_remove(c->item); /* release the c->item reference */
- c->item = 0;
-}
-
-static void process_bin_touch(conn *c) {
- item *it;
-
- protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->wbuf;
- char* key = binary_get_key(c);
- size_t nkey = c->binary_header.request.keylen;
- protocol_binary_request_touch *t = binary_get_request(c);
- time_t exptime = ntohl(t->message.body.expiration);
-
- if (settings.verbose > 1) {
- int ii;
- /* May be GAT/GATQ/etc */
- fprintf(stderr, "<%d TOUCH ", c->sfd);
- for (ii = 0; ii < nkey; ++ii) {
- fprintf(stderr, "%c", key[ii]);
- }
- fprintf(stderr, "\n");
- }
-
- it = item_touch(key, nkey, realtime(exptime));
-
- if (it) {
- /* the length has two unnecessary bytes ("\r\n") */
- uint16_t keylen = 0;
- uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2);
-
- item_update(it);
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.touch_cmds++;
- c->thread->stats.slab_stats[it->slabs_clsid].touch_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- MEMCACHED_COMMAND_TOUCH(c->sfd, ITEM_key(it), it->nkey,
- it->nbytes, ITEM_get_cas(it));
-
- if (c->cmd == PROTOCOL_BINARY_CMD_TOUCH) {
- bodylen -= it->nbytes - 2;
- } else if (c->cmd == PROTOCOL_BINARY_CMD_GATK) {
- bodylen += nkey;
- keylen = nkey;
- }
-
- add_bin_header(c, 0, sizeof(rsp->message.body), keylen, bodylen);
- rsp->message.header.response.cas = htonll(ITEM_get_cas(it));
-
- // add the flags
- rsp->message.body.flags = htonl(strtoul(ITEM_suffix(it), NULL, 10));
- add_iov(c, &rsp->message.body, sizeof(rsp->message.body));
-
- if (c->cmd == PROTOCOL_BINARY_CMD_GATK) {
- add_iov(c, ITEM_key(it), nkey);
- }
-
- /* Add the data minus the CRLF */
- if (c->cmd != PROTOCOL_BINARY_CMD_TOUCH) {
- add_iov(c, ITEM_data(it), it->nbytes - 2);
- }
-
- conn_set_state(c, conn_mwrite);
- c->write_and_go = conn_new_cmd;
- /* Remember this command so we can garbage collect it later */
- c->item = it;
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.touch_cmds++;
- c->thread->stats.touch_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- MEMCACHED_COMMAND_TOUCH(c->sfd, key, nkey, -1, 0);
-
- if (c->noreply) {
- conn_set_state(c, conn_new_cmd);
- } else {
- if (c->cmd == PROTOCOL_BINARY_CMD_GATK) {
- char *ofs = c->wbuf + sizeof(protocol_binary_response_header);
- add_bin_header(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
- 0, nkey, nkey);
- memcpy(ofs, key, nkey);
- add_iov(c, ofs, nkey);
- conn_set_state(c, conn_mwrite);
- c->write_and_go = conn_new_cmd;
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- }
- }
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_get(key, nkey, NULL != it);
- }
-}
-
-static void process_bin_get(conn *c) {
- item *it;
-
- protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->wbuf;
- char* key = binary_get_key(c);
- size_t nkey = c->binary_header.request.keylen;
-
- if (settings.verbose > 1) {
- int ii;
- fprintf(stderr, "<%d GET ", c->sfd);
- for (ii = 0; ii < nkey; ++ii) {
- fprintf(stderr, "%c", key[ii]);
- }
- fprintf(stderr, "\n");
- }
-
- it = item_get(key, nkey);
- if (it) {
- /* the length has two unnecessary bytes ("\r\n") */
- uint16_t keylen = 0;
- uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2);
-
- item_update(it);
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.get_cmds++;
- c->thread->stats.slab_stats[it->slabs_clsid].get_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
- it->nbytes, ITEM_get_cas(it));
-
- if (c->cmd == PROTOCOL_BINARY_CMD_GETK) {
- bodylen += nkey;
- keylen = nkey;
- }
- add_bin_header(c, 0, sizeof(rsp->message.body), keylen, bodylen);
- rsp->message.header.response.cas = htonll(ITEM_get_cas(it));
-
- // add the flags
- rsp->message.body.flags = htonl(strtoul(ITEM_suffix(it), NULL, 10));
- add_iov(c, &rsp->message.body, sizeof(rsp->message.body));
-
- if (c->cmd == PROTOCOL_BINARY_CMD_GETK) {
- add_iov(c, ITEM_key(it), nkey);
- }
-
- /* Add the data minus the CRLF */
- add_iov(c, ITEM_data(it), it->nbytes - 2);
- conn_set_state(c, conn_mwrite);
- c->write_and_go = conn_new_cmd;
- /* Remember this command so we can garbage collect it later */
- c->item = it;
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.get_cmds++;
- c->thread->stats.get_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
-
- if (c->noreply) {
- conn_set_state(c, conn_new_cmd);
- } else {
- if (c->cmd == PROTOCOL_BINARY_CMD_GETK) {
- char *ofs = c->wbuf + sizeof(protocol_binary_response_header);
- add_bin_header(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
- 0, nkey, nkey);
- memcpy(ofs, key, nkey);
- add_iov(c, ofs, nkey);
- conn_set_state(c, conn_mwrite);
- c->write_and_go = conn_new_cmd;
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- }
- }
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_get(key, nkey, NULL != it);
- }
-}
-
-static void append_bin_stats(const char *key, const uint16_t klen,
- const char *val, const uint32_t vlen,
- conn *c) {
- char *buf = c->stats.buffer + c->stats.offset;
- uint32_t bodylen = klen + vlen;
- protocol_binary_response_header header = {
- .response.magic = (uint8_t)PROTOCOL_BINARY_RES,
- .response.opcode = PROTOCOL_BINARY_CMD_STAT,
- .response.keylen = (uint16_t)htons(klen),
- .response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES,
- .response.bodylen = htonl(bodylen),
- .response.opaque = c->opaque
- };
-
- memcpy(buf, header.bytes, sizeof(header.response));
- buf += sizeof(header.response);
-
- if (klen > 0) {
- memcpy(buf, key, klen);
- buf += klen;
-
- if (vlen > 0) {
- memcpy(buf, val, vlen);
- }
- }
-
- c->stats.offset += sizeof(header.response) + bodylen;
-}
-
-static void append_ascii_stats(const char *key, const uint16_t klen,
- const char *val, const uint32_t vlen,
- conn *c) {
- char *pos = c->stats.buffer + c->stats.offset;
- uint32_t nbytes = 0;
- int remaining = c->stats.size - c->stats.offset;
- int room = remaining - 1;
-
- if (klen == 0 && vlen == 0) {
- nbytes = snprintf(pos, room, "END\r\n");
- } else if (vlen == 0) {
- nbytes = snprintf(pos, room, "STAT %s\r\n", key);
- } else {
- nbytes = snprintf(pos, room, "STAT %s %s\r\n", key, val);
- }
-
- c->stats.offset += nbytes;
-}
-
-static bool grow_stats_buf(conn *c, size_t needed) {
- size_t nsize = c->stats.size;
- size_t available = nsize - c->stats.offset;
- bool rv = true;
-
- /* Special case: No buffer -- need to allocate fresh */
- if (c->stats.buffer == NULL) {
- nsize = 1024;
- available = c->stats.size = c->stats.offset = 0;
- }
-
- while (needed > available) {
- assert(nsize > 0);
- nsize = nsize << 1;
- available = nsize - c->stats.offset;
- }
-
- if (nsize != c->stats.size) {
- char *ptr = realloc(c->stats.buffer, nsize);
- if (ptr) {
- c->stats.buffer = ptr;
- c->stats.size = nsize;
- } else {
- rv = false;
- }
- }
-
- return rv;
-}
-
-static void append_stats(const char *key, const uint16_t klen,
- const char *val, const uint32_t vlen,
- const void *cookie)
-{
- /* value without a key is invalid */
- if (klen == 0 && vlen > 0) {
- return ;
- }
-
- conn *c = (conn*)cookie;
-
- if (c->protocol == binary_prot) {
- size_t needed = vlen + klen + sizeof(protocol_binary_response_header);
- if (!grow_stats_buf(c, needed)) {
- return ;
- }
- append_bin_stats(key, klen, val, vlen, c);
- } else {
- size_t needed = vlen + klen + 10; // 10 == "STAT = \r\n"
- if (!grow_stats_buf(c, needed)) {
- return ;
- }
- append_ascii_stats(key, klen, val, vlen, c);
- }
-
- assert(c->stats.offset <= c->stats.size);
-}
-
-static void process_bin_stat(conn *c) {
- char *subcommand = binary_get_key(c);
- size_t nkey = c->binary_header.request.keylen;
-
- if (settings.verbose > 1) {
- int ii;
- fprintf(stderr, "<%d STATS ", c->sfd);
- for (ii = 0; ii < nkey; ++ii) {
- fprintf(stderr, "%c", subcommand[ii]);
- }
- fprintf(stderr, "\n");
- }
-
- if (nkey == 0) {
- /* request all statistics */
- server_stats(&append_stats, c);
- (void)get_stats(NULL, 0, &append_stats, c);
- } else if (strncmp(subcommand, "reset", 5) == 0) {
- stats_reset();
- } else if (strncmp(subcommand, "settings", 8) == 0) {
- process_stat_settings(&append_stats, c);
- } else if (strncmp(subcommand, "detail", 6) == 0) {
- char *subcmd_pos = subcommand + 6;
- if (strncmp(subcmd_pos, " dump", 5) == 0) {
- int len;
- char *dump_buf = stats_prefix_dump(&len);
- if (dump_buf == NULL || len <= 0) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, 0);
- return ;
- } else {
- append_stats("detailed", strlen("detailed"), dump_buf, len, c);
- free(dump_buf);
- }
- } else if (strncmp(subcmd_pos, " on", 3) == 0) {
- settings.detail_enabled = 1;
- } else if (strncmp(subcmd_pos, " off", 4) == 0) {
- settings.detail_enabled = 0;
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- return;
- }
- } else {
- if (get_stats(subcommand, nkey, &append_stats, c)) {
- if (c->stats.buffer == NULL) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, 0);
- } else {
- write_and_free(c, c->stats.buffer, c->stats.offset);
- c->stats.buffer = NULL;
- }
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- }
-
- return;
- }
-
- /* Append termination package and start the transfer */
- append_stats(NULL, 0, NULL, 0, c);
- if (c->stats.buffer == NULL) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, 0);
- } else {
- write_and_free(c, c->stats.buffer, c->stats.offset);
- c->stats.buffer = NULL;
- }
-}
-
-static void bin_read_key(conn *c, enum bin_substates next_substate, int extra) {
- assert(c);
- c->substate = next_substate;
- c->rlbytes = c->keylen + extra;
-
- /* Ok... do we have room for the extras and the key in the input buffer? */
- ptrdiff_t offset = c->rcurr + sizeof(protocol_binary_request_header) - c->rbuf;
- if (c->rlbytes > c->rsize - offset) {
- size_t nsize = c->rsize;
- size_t size = c->rlbytes + sizeof(protocol_binary_request_header);
-
- while (size > nsize) {
- nsize *= 2;
- }
-
- if (nsize != c->rsize) {
- if (settings.verbose > 1) {
- fprintf(stderr, "%d: Need to grow buffer from %lu to %lu\n",
- c->sfd, (unsigned long)c->rsize, (unsigned long)nsize);
- }
- char *newm = realloc(c->rbuf, nsize);
- if (newm == NULL) {
- if (settings.verbose) {
- fprintf(stderr, "%d: Failed to grow buffer.. closing connection\n",
- c->sfd);
- }
- conn_set_state(c, conn_closing);
- return;
- }
-
- c->rbuf= newm;
- /* rcurr should point to the same offset in the packet */
- c->rcurr = c->rbuf + offset - sizeof(protocol_binary_request_header);
- c->rsize = nsize;
- }
- if (c->rbuf != c->rcurr) {
- memmove(c->rbuf, c->rcurr, c->rbytes);
- c->rcurr = c->rbuf;
- if (settings.verbose > 1) {
- fprintf(stderr, "%d: Repack input buffer\n", c->sfd);
- }
- }
- }
-
- /* preserve the header in the buffer.. */
- c->ritem = c->rcurr + sizeof(protocol_binary_request_header);
- conn_set_state(c, conn_nread);
-}
-
-/* Just write an error message and disconnect the client */
-static void handle_binary_protocol_error(conn *c) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, 0);
- if (settings.verbose) {
- fprintf(stderr, "Protocol error (opcode %02x), close connection %d\n",
- c->binary_header.request.opcode, c->sfd);
- }
- c->write_and_go = conn_closing;
-}
-
-static void init_sasl_conn(conn *c) {
- assert(c);
- /* should something else be returned? */
- if (!settings.sasl)
- return;
-
- if (!c->sasl_conn) {
- int result=sasl_server_new("memcached",
- NULL,
- my_sasl_hostname[0] ? my_sasl_hostname : NULL,
- NULL, NULL,
- NULL, 0, &c->sasl_conn);
- if (result != SASL_OK) {
- if (settings.verbose) {
- fprintf(stderr, "Failed to initialize SASL conn.\n");
- }
- c->sasl_conn = NULL;
- }
- }
-}
-
-static void bin_list_sasl_mechs(conn *c) {
- // Guard against a disabled SASL.
- if (!settings.sasl) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND,
- c->binary_header.request.bodylen
- - c->binary_header.request.keylen);
- return;
- }
-
- init_sasl_conn(c);
- const char *result_string = NULL;
- unsigned int string_length = 0;
- int result=sasl_listmech(c->sasl_conn, NULL,
- "", /* What to prepend the string with */
- " ", /* What to separate mechanisms with */
- "", /* What to append to the string */
- &result_string, &string_length,
- NULL);
- if (result != SASL_OK) {
- /* Perhaps there's a better error for this... */
- if (settings.verbose) {
- fprintf(stderr, "Failed to list SASL mechanisms.\n");
- }
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
- return;
- }
- write_bin_response(c, (char*)result_string, 0, 0, string_length);
-}
-
-static void process_bin_sasl_auth(conn *c) {
- // Guard for handling disabled SASL on the server.
- if (!settings.sasl) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND,
- c->binary_header.request.bodylen
- - c->binary_header.request.keylen);
- return;
- }
-
- assert(c->binary_header.request.extlen == 0);
-
- int nkey = c->binary_header.request.keylen;
- int vlen = c->binary_header.request.bodylen - nkey;
-
- if (nkey > MAX_SASL_MECH_LEN) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, vlen);
- c->write_and_go = conn_swallow;
- return;
- }
-
- char *key = binary_get_key(c);
- assert(key);
-
- item *it = item_alloc(key, nkey, 0, 0, vlen);
-
- if (it == 0) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, vlen);
- c->write_and_go = conn_swallow;
- return;
- }
-
- c->item = it;
- c->ritem = ITEM_data(it);
- c->rlbytes = vlen;
- conn_set_state(c, conn_nread);
- c->substate = bin_reading_sasl_auth_data;
-}
-
-static void process_bin_complete_sasl_auth(conn *c) {
- assert(settings.sasl);
- const char *out = NULL;
- unsigned int outlen = 0;
-
- assert(c->item);
- init_sasl_conn(c);
-
- int nkey = c->binary_header.request.keylen;
- int vlen = c->binary_header.request.bodylen - nkey;
-
- char mech[nkey+1];
- memcpy(mech, ITEM_key((item*)c->item), nkey);
- mech[nkey] = 0x00;
-
- if (settings.verbose)
- fprintf(stderr, "mech: ``%s'' with %d bytes of data\n", mech, vlen);
-
- const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item);
-
- int result=-1;
-
- switch (c->cmd) {
- case PROTOCOL_BINARY_CMD_SASL_AUTH:
- result = sasl_server_start(c->sasl_conn, mech,
- challenge, vlen,
- &out, &outlen);
- break;
- case PROTOCOL_BINARY_CMD_SASL_STEP:
- result = sasl_server_step(c->sasl_conn,
- challenge, vlen,
- &out, &outlen);
- break;
- default:
- assert(false); /* CMD should be one of the above */
- /* This code is pretty much impossible, but makes the compiler
- happier */
- if (settings.verbose) {
- fprintf(stderr, "Unhandled command %d with challenge %s\n",
- c->cmd, challenge);
- }
- break;
- }
-
- item_unlink(c->item);
-
- if (settings.verbose) {
- fprintf(stderr, "sasl result code: %d\n", result);
- }
-
- switch(result) {
- case SASL_OK:
- write_bin_response(c, "Authenticated", 0, 0, strlen("Authenticated"));
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.auth_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- break;
- case SASL_CONTINUE:
- add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen);
- if(outlen > 0) {
- add_iov(c, out, outlen);
- }
- conn_set_state(c, conn_mwrite);
- c->write_and_go = conn_new_cmd;
- break;
- default:
- if (settings.verbose)
- fprintf(stderr, "Unknown sasl response: %d\n", result);
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.auth_cmds++;
- c->thread->stats.auth_errors++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- }
-}
-
-static bool authenticated(conn *c) {
- assert(settings.sasl);
- bool rv = false;
-
- switch (c->cmd) {
- case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_SASL_AUTH: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_SASL_STEP: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_VERSION: /* FALLTHROUGH */
- rv = true;
- break;
- default:
- if (c->sasl_conn) {
- const void *uname = NULL;
- sasl_getprop(c->sasl_conn, SASL_USERNAME, &uname);
- rv = uname != NULL;
- }
- }
-
- if (settings.verbose > 1) {
- fprintf(stderr, "authenticated() in cmd 0x%02x is %s\n",
- c->cmd, rv ? "true" : "false");
- }
-
- return rv;
-}
-
-static void dispatch_bin_command(conn *c) {
- int protocol_error = 0;
-
- int extlen = c->binary_header.request.extlen;
- int keylen = c->binary_header.request.keylen;
- uint32_t bodylen = c->binary_header.request.bodylen;
-
- if (settings.sasl && !authenticated(c)) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, 0);
- c->write_and_go = conn_closing;
- return;
- }
-
- MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
- c->noreply = true;
-
- /* binprot supports 16bit keys, but internals are still 8bit */
- if (keylen > KEY_MAX_LENGTH) {
- handle_binary_protocol_error(c);
- return;
- }
-
- switch (c->cmd) {
- case PROTOCOL_BINARY_CMD_SETQ:
- c->cmd = PROTOCOL_BINARY_CMD_SET;
- break;
- case PROTOCOL_BINARY_CMD_ADDQ:
- c->cmd = PROTOCOL_BINARY_CMD_ADD;
- break;
- case PROTOCOL_BINARY_CMD_REPLACEQ:
- c->cmd = PROTOCOL_BINARY_CMD_REPLACE;
- break;
- case PROTOCOL_BINARY_CMD_DELETEQ:
- c->cmd = PROTOCOL_BINARY_CMD_DELETE;
- break;
- case PROTOCOL_BINARY_CMD_INCREMENTQ:
- c->cmd = PROTOCOL_BINARY_CMD_INCREMENT;
- break;
- case PROTOCOL_BINARY_CMD_DECREMENTQ:
- c->cmd = PROTOCOL_BINARY_CMD_DECREMENT;
- break;
- case PROTOCOL_BINARY_CMD_QUITQ:
- c->cmd = PROTOCOL_BINARY_CMD_QUIT;
- break;
- case PROTOCOL_BINARY_CMD_FLUSHQ:
- c->cmd = PROTOCOL_BINARY_CMD_FLUSH;
- break;
- case PROTOCOL_BINARY_CMD_APPENDQ:
- c->cmd = PROTOCOL_BINARY_CMD_APPEND;
- break;
- case PROTOCOL_BINARY_CMD_PREPENDQ:
- c->cmd = PROTOCOL_BINARY_CMD_PREPEND;
- break;
- case PROTOCOL_BINARY_CMD_GETQ:
- c->cmd = PROTOCOL_BINARY_CMD_GET;
- break;
- case PROTOCOL_BINARY_CMD_GETKQ:
- c->cmd = PROTOCOL_BINARY_CMD_GETK;
- break;
- case PROTOCOL_BINARY_CMD_GATQ:
- c->cmd = PROTOCOL_BINARY_CMD_GAT;
- break;
- case PROTOCOL_BINARY_CMD_GATKQ:
- c->cmd = PROTOCOL_BINARY_CMD_GAT;
- break;
- default:
- c->noreply = false;
- }
-
- switch (c->cmd) {
- case PROTOCOL_BINARY_CMD_VERSION:
- if (extlen == 0 && keylen == 0 && bodylen == 0) {
- write_bin_response(c, VERSION, 0, 0, strlen(VERSION));
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_FLUSH:
- if (keylen == 0 && bodylen == extlen && (extlen == 0 || extlen == 4)) {
- bin_read_key(c, bin_read_flush_exptime, extlen);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_NOOP:
- if (extlen == 0 && keylen == 0 && bodylen == 0) {
- write_bin_response(c, NULL, 0, 0, 0);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_SET: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_ADD: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_REPLACE:
- if (extlen == 8 && keylen != 0 && bodylen >= (keylen + 8)) {
- bin_read_key(c, bin_reading_set_header, 8);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_GETQ: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_GET: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_GETKQ: /* FALLTHROUGH */
- case PROTOCOL_BINARY_CMD_GETK:
- if (extlen == 0 && bodylen == keylen && keylen > 0) {
- bin_read_key(c, bin_reading_get_key, 0);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_DELETE:
- if (keylen > 0 && extlen == 0 && bodylen == keylen) {
- bin_read_key(c, bin_reading_del_header, extlen);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_INCREMENT:
- case PROTOCOL_BINARY_CMD_DECREMENT:
- if (keylen > 0 && extlen == 20 && bodylen == (keylen + extlen)) {
- bin_read_key(c, bin_reading_incr_header, 20);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_APPEND:
- case PROTOCOL_BINARY_CMD_PREPEND:
- if (keylen > 0 && extlen == 0) {
- bin_read_key(c, bin_reading_set_header, 0);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_STAT:
- if (extlen == 0) {
- bin_read_key(c, bin_reading_stat, 0);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_QUIT:
- if (keylen == 0 && extlen == 0 && bodylen == 0) {
- write_bin_response(c, NULL, 0, 0, 0);
- c->write_and_go = conn_closing;
- if (c->noreply) {
- conn_set_state(c, conn_closing);
- }
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
- if (extlen == 0 && keylen == 0 && bodylen == 0) {
- bin_list_sasl_mechs(c);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_SASL_AUTH:
- case PROTOCOL_BINARY_CMD_SASL_STEP:
- if (extlen == 0 && keylen != 0) {
- bin_read_key(c, bin_reading_sasl_auth, 0);
- } else {
- protocol_error = 1;
- }
- break;
- case PROTOCOL_BINARY_CMD_TOUCH:
- case PROTOCOL_BINARY_CMD_GAT:
- case PROTOCOL_BINARY_CMD_GATQ:
- case PROTOCOL_BINARY_CMD_GATK:
- case PROTOCOL_BINARY_CMD_GATKQ:
- if (extlen == 4 && keylen != 0) {
- bin_read_key(c, bin_reading_touch_key, 4);
- } else {
- protocol_error = 1;
- }
- break;
- default:
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, bodylen);
- }
-
- if (protocol_error)
- handle_binary_protocol_error(c);
-}
-
-static void process_bin_update(conn *c) {
- char *key;
- int nkey;
- int vlen;
- item *it;
- protocol_binary_request_set* req = binary_get_request(c);
-
- assert(c != NULL);
-
- key = binary_get_key(c);
- nkey = c->binary_header.request.keylen;
-
- /* fix byteorder in the request */
- req->message.body.flags = ntohl(req->message.body.flags);
- req->message.body.expiration = ntohl(req->message.body.expiration);
-
- vlen = c->binary_header.request.bodylen - (nkey + c->binary_header.request.extlen);
-
- if (settings.verbose > 1) {
- int ii;
- if (c->cmd == PROTOCOL_BINARY_CMD_ADD) {
- fprintf(stderr, "<%d ADD ", c->sfd);
- } else if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
- fprintf(stderr, "<%d SET ", c->sfd);
- } else {
- fprintf(stderr, "<%d REPLACE ", c->sfd);
- }
- for (ii = 0; ii < nkey; ++ii) {
- fprintf(stderr, "%c", key[ii]);
- }
-
- fprintf(stderr, " Value len is %d", vlen);
- fprintf(stderr, "\n");
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_set(key, nkey);
- }
-
- it = item_alloc(key, nkey, req->message.body.flags,
- realtime(req->message.body.expiration), vlen+2);
-
- if (it == 0) {
- if (! item_size_ok(nkey, req->message.body.flags, vlen + 2)) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, vlen);
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, vlen);
- }
-
- /* Avoid stale data persisting in cache because we failed alloc.
- * Unacceptable for SET. Anywhere else too? */
- if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
- it = item_get(key, nkey);
- if (it) {
- item_unlink(it);
- item_remove(it);
- }
- }
-
- /* swallow the data line */
- c->write_and_go = conn_swallow;
- return;
- }
-
- ITEM_set_cas(it, c->binary_header.request.cas);
-
- switch (c->cmd) {
- case PROTOCOL_BINARY_CMD_ADD:
- c->cmd = NREAD_ADD;
- break;
- case PROTOCOL_BINARY_CMD_SET:
- c->cmd = NREAD_SET;
- break;
- case PROTOCOL_BINARY_CMD_REPLACE:
- c->cmd = NREAD_REPLACE;
- break;
- default:
- assert(0);
- }
-
- if (ITEM_get_cas(it) != 0) {
- c->cmd = NREAD_CAS;
- }
-
- c->item = it;
- c->ritem = ITEM_data(it);
- c->rlbytes = vlen;
- conn_set_state(c, conn_nread);
- c->substate = bin_read_set_value;
-}
-
-static void process_bin_append_prepend(conn *c) {
- char *key;
- int nkey;
- int vlen;
- item *it;
-
- assert(c != NULL);
-
- key = binary_get_key(c);
- nkey = c->binary_header.request.keylen;
- vlen = c->binary_header.request.bodylen - nkey;
-
- if (settings.verbose > 1) {
- fprintf(stderr, "Value len is %d\n", vlen);
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_set(key, nkey);
- }
-
- it = item_alloc(key, nkey, 0, 0, vlen+2);
-
- if (it == 0) {
- if (! item_size_ok(nkey, 0, vlen + 2)) {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, vlen);
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, vlen);
- }
- /* swallow the data line */
- c->write_and_go = conn_swallow;
- return;
- }
-
- ITEM_set_cas(it, c->binary_header.request.cas);
-
- switch (c->cmd) {
- case PROTOCOL_BINARY_CMD_APPEND:
- c->cmd = NREAD_APPEND;
- break;
- case PROTOCOL_BINARY_CMD_PREPEND:
- c->cmd = NREAD_PREPEND;
- break;
- default:
- assert(0);
- }
-
- c->item = it;
- c->ritem = ITEM_data(it);
- c->rlbytes = vlen;
- conn_set_state(c, conn_nread);
- c->substate = bin_read_set_value;
-}
-
-static void process_bin_flush(conn *c) {
- time_t exptime = 0;
- protocol_binary_request_flush* req = binary_get_request(c);
-
- if (c->binary_header.request.extlen == sizeof(req->message.body)) {
- exptime = ntohl(req->message.body.expiration);
- }
-
- if (exptime > 0) {
- settings.oldest_live = realtime(exptime) - 1;
- } else {
- settings.oldest_live = current_time - 1;
- }
- item_flush_expired();
-
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.flush_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- write_bin_response(c, NULL, 0, 0, 0);
-}
-
-static void process_bin_delete(conn *c) {
- item *it;
-
- protocol_binary_request_delete* req = binary_get_request(c);
-
- char* key = binary_get_key(c);
- size_t nkey = c->binary_header.request.keylen;
-
- assert(c != NULL);
-
- if (settings.verbose > 1) {
- fprintf(stderr, "Deleting %s\n", key);
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_delete(key, nkey);
- }
-
- it = item_get(key, nkey);
- if (it) {
- uint64_t cas = ntohll(req->message.header.request.cas);
- if (cas == 0 || cas == ITEM_get_cas(it)) {
- MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[it->slabs_clsid].delete_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- item_unlink(it);
- write_bin_response(c, NULL, 0, 0, 0);
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, 0);
- }
- item_remove(it); /* release our reference */
- } else {
- write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, 0);
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.delete_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- }
-}
-
-static void complete_nread_binary(conn *c) {
- assert(c != NULL);
- assert(c->cmd >= 0);
-
- switch(c->substate) {
- case bin_reading_set_header:
- if (c->cmd == PROTOCOL_BINARY_CMD_APPEND ||
- c->cmd == PROTOCOL_BINARY_CMD_PREPEND) {
- process_bin_append_prepend(c);
- } else {
- process_bin_update(c);
- }
- break;
- case bin_read_set_value:
- complete_update_bin(c);
- break;
- case bin_reading_get_key:
- process_bin_get(c);
- break;
- case bin_reading_touch_key:
- process_bin_touch(c);
- break;
- case bin_reading_stat:
- process_bin_stat(c);
- break;
- case bin_reading_del_header:
- process_bin_delete(c);
- break;
- case bin_reading_incr_header:
- complete_incr_bin(c);
- break;
- case bin_read_flush_exptime:
- process_bin_flush(c);
- break;
- case bin_reading_sasl_auth:
- process_bin_sasl_auth(c);
- break;
- case bin_reading_sasl_auth_data:
- process_bin_complete_sasl_auth(c);
- break;
- default:
- fprintf(stderr, "Not handling substate %d\n", c->substate);
- assert(0);
- }
-}
-
-static void reset_cmd_handler(conn *c) {
- c->cmd = -1;
- c->substate = bin_no_state;
- if(c->item != NULL) {
- item_remove(c->item);
- c->item = NULL;
- }
- conn_shrink(c);
- if (c->rbytes > 0) {
- conn_set_state(c, conn_parse_cmd);
- } else {
- conn_set_state(c, conn_waiting);
- }
-}
-
-static void complete_nread(conn *c) {
- assert(c != NULL);
- assert(c->protocol == ascii_prot
- || c->protocol == binary_prot);
-
- if (c->protocol == ascii_prot) {
- complete_nread_ascii(c);
- } else if (c->protocol == binary_prot) {
- complete_nread_binary(c);
- }
-}
-
-/*
- * Stores an item in the cache according to the semantics of one of the set
- * commands. In threaded mode, this is protected by the cache lock.
- *
- * Returns the state of storage.
- */
-enum store_item_type do_store_item(item *it, int comm, conn *c, const uint32_t hv) {
- char *key = ITEM_key(it);
- item *old_it = do_item_get(key, it->nkey, hv);
- enum store_item_type stored = NOT_STORED;
-
- item *new_it = NULL;
- int flags;
-
- if (old_it != NULL && comm == NREAD_ADD) {
- /* add only adds a nonexistent item, but promote to head of LRU */
- do_item_update(old_it);
- } else if (!old_it && (comm == NREAD_REPLACE
- || comm == NREAD_APPEND || comm == NREAD_PREPEND))
- {
- /* replace only replaces an existing value; don't store */
- } else if (comm == NREAD_CAS) {
- /* validate cas operation */
- if(old_it == NULL) {
- // LRU expired
- stored = NOT_FOUND;
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.cas_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- }
- else if (ITEM_get_cas(it) == ITEM_get_cas(old_it)) {
- // cas validates
- // it and old_it may belong to different classes.
- // I'm updating the stats for the one that's getting pushed out
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[old_it->slabs_clsid].cas_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- item_replace(old_it, it, hv);
- stored = STORED;
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[old_it->slabs_clsid].cas_badval++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- if(settings.verbose > 1) {
- fprintf(stderr, "CAS: failure: expected %llu, got %llu\n",
- (unsigned long long)ITEM_get_cas(old_it),
- (unsigned long long)ITEM_get_cas(it));
- }
- stored = EXISTS;
- }
- } else {
- /*
- * Append - combine new and old record into single one. Here it's
- * atomic and thread-safe.
- */
- if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {
- /*
- * Validate CAS
- */
- if (ITEM_get_cas(it) != 0) {
- // CAS much be equal
- if (ITEM_get_cas(it) != ITEM_get_cas(old_it)) {
- stored = EXISTS;
- }
- }
-
- if (stored == NOT_STORED) {
- /* we have it and old_it here - alloc memory to hold both */
- /* flags was already lost - so recover them from ITEM_suffix(it) */
-
- flags = (int) strtol(ITEM_suffix(old_it), (char **) NULL, 10);
-
- new_it = item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);
-
- if (new_it == NULL) {
- /* SERVER_ERROR out of memory */
- if (old_it != NULL)
- do_item_remove(old_it);
-
- return NOT_STORED;
- }
-
- /* copy data from it and old_it to new_it */
-
- if (comm == NREAD_APPEND) {
- memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
- memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);
- } else {
- /* NREAD_PREPEND */
- memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);
- memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
- }
-
- it = new_it;
- }
- }
-
- if (stored == NOT_STORED) {
- if (old_it != NULL)
- item_replace(old_it, it, hv);
- else
- do_item_link(it, hv);
-
- c->cas = ITEM_get_cas(it);
-
- stored = STORED;
- }
- }
-
- if (old_it != NULL)
- do_item_remove(old_it); /* release our reference */
- if (new_it != NULL)
- do_item_remove(new_it);
-
- if (stored == STORED) {
- c->cas = ITEM_get_cas(it);
- }
-
- return stored;
-}
-
-typedef struct token_s {
- char *value;
- size_t length;
-} token_t;
-
-#define COMMAND_TOKEN 0
-#define SUBCOMMAND_TOKEN 1
-#define KEY_TOKEN 1
-
-#define MAX_TOKENS 8
-
-/*
- * Tokenize the command string by replacing whitespace with '\0' and update
- * the token array tokens with pointer to start of each token and length.
- * Returns total number of tokens. The last valid token is the terminal
- * token (value points to the first unprocessed character of the string and
- * length zero).
- *
- * Usage example:
- *
- * while(tokenize_command(command, ncommand, tokens, max_tokens) > 0) {
- * for(int ix = 0; tokens[ix].length != 0; ix++) {
- * ...
- * }
- * ncommand = tokens[ix].value - command;
- * command = tokens[ix].value;
- * }
- */
-static size_t tokenize_command(char *command, token_t *tokens, const size_t max_tokens) {
- char *s, *e;
- size_t ntokens = 0;
- size_t len = strlen(command);
- unsigned int i = 0;
-
- assert(command != NULL && tokens != NULL && max_tokens > 1);
-
- s = e = command;
- for (i = 0; i < len; i++) {
- if (*e == ' ') {
- if (s != e) {
- tokens[ntokens].value = s;
- tokens[ntokens].length = e - s;
- ntokens++;
- *e = '\0';
- if (ntokens == max_tokens - 1) {
- e++;
- s = e; /* so we don't add an extra token */
- break;
- }
- }
- s = e + 1;
- }
- e++;
- }
-
- if (s != e) {
- tokens[ntokens].value = s;
- tokens[ntokens].length = e - s;
- ntokens++;
- }
-
- /*
- * If we scanned the whole string, the terminal value pointer is null,
- * otherwise it is the first unprocessed character.
- */
- tokens[ntokens].value = *e == '\0' ? NULL : e;
- tokens[ntokens].length = 0;
- ntokens++;
-
- return ntokens;
-}
-
-/* set up a connection to write a buffer then free it, used for stats */
-static void write_and_free(conn *c, char *buf, int bytes) {
- if (buf) {
- c->write_and_free = buf;
- c->wcurr = buf;
- c->wbytes = bytes;
- conn_set_state(c, conn_write);
- c->write_and_go = conn_new_cmd;
- } else {
- out_string(c, "SERVER_ERROR out of memory writing stats");
- }
-}
-
-static inline bool set_noreply_maybe(conn *c, token_t *tokens, size_t ntokens)
-{
- int noreply_index = ntokens - 2;
-
- /*
- NOTE: this function is not the first place where we are going to
- send the reply. We could send it instead from process_command()
- if the request line has wrong number of tokens. However parsing
- malformed line for "noreply" option is not reliable anyway, so
- it can't be helped.
- */
- if (tokens[noreply_index].value
- && strcmp(tokens[noreply_index].value, "noreply") == 0) {
- c->noreply = true;
- }
- return c->noreply;
-}
-
-void append_stat(const char *name, ADD_STAT add_stats, conn *c,
- const char *fmt, ...) {
- char val_str[STAT_VAL_LEN];
- int vlen;
- va_list ap;
-
- assert(name);
- assert(add_stats);
- assert(c);
- assert(fmt);
-
- va_start(ap, fmt);
- vlen = vsnprintf(val_str, sizeof(val_str) - 1, fmt, ap);
- va_end(ap);
-
- add_stats(name, strlen(name), val_str, vlen, c);
-}
-
-inline static void process_stats_detail(conn *c, const char *command) {
- assert(c != NULL);
-
- if (strcmp(command, "on") == 0) {
- settings.detail_enabled = 1;
- out_string(c, "OK");
- }
- else if (strcmp(command, "off") == 0) {
- settings.detail_enabled = 0;
- out_string(c, "OK");
- }
- else if (strcmp(command, "dump") == 0) {
- int len;
- char *stats = stats_prefix_dump(&len);
- write_and_free(c, stats, len);
- }
- else {
- out_string(c, "CLIENT_ERROR usage: stats detail on|off|dump");
- }
-}
-
-/* return server specific stats only */
-static void server_stats(ADD_STAT add_stats, conn *c) {
- pid_t pid = getpid();
- rel_time_t now = current_time;
-
- struct thread_stats thread_stats;
- threadlocal_stats_aggregate(&thread_stats);
- struct slab_stats slab_stats;
- slab_stats_aggregate(&thread_stats, &slab_stats);
-
-#ifndef WIN32
- struct rusage usage;
- getrusage(RUSAGE_SELF, &usage);
-#endif /* !WIN32 */
-
- STATS_LOCK();
-
- APPEND_STAT("pid", "%lu", (long)pid);
- APPEND_STAT("uptime", "%u", now);
- APPEND_STAT("time", "%ld", now + (long)process_started);
- APPEND_STAT("version", "%s", VERSION);
- APPEND_STAT("libevent", "%s", event_get_version());
- APPEND_STAT("pointer_size", "%d", (int)(8 * sizeof(void *)));
-
-#ifndef WIN32
- append_stat("rusage_user", add_stats, c, "%ld.%06ld",
- (long)usage.ru_utime.tv_sec,
- (long)usage.ru_utime.tv_usec);
- append_stat("rusage_system", add_stats, c, "%ld.%06ld",
- (long)usage.ru_stime.tv_sec,
- (long)usage.ru_stime.tv_usec);
-#endif /* !WIN32 */
-
- APPEND_STAT("curr_connections", "%u", stats.curr_conns - 1);
- APPEND_STAT("total_connections", "%u", stats.total_conns);
- if (settings.maxconns_fast) {
- APPEND_STAT("rejected_connections", "%llu", (unsigned long long)stats.rejected_conns);
- }
- APPEND_STAT("connection_structures", "%u", stats.conn_structs);
- APPEND_STAT("reserved_fds", "%u", stats.reserved_fds);
- APPEND_STAT("cmd_get", "%llu", (unsigned long long)thread_stats.get_cmds);
- APPEND_STAT("cmd_set", "%llu", (unsigned long long)slab_stats.set_cmds);
- APPEND_STAT("cmd_flush", "%llu", (unsigned long long)thread_stats.flush_cmds);
- APPEND_STAT("cmd_touch", "%llu", (unsigned long long)thread_stats.touch_cmds);
- APPEND_STAT("get_hits", "%llu", (unsigned long long)slab_stats.get_hits);
- APPEND_STAT("get_misses", "%llu", (unsigned long long)thread_stats.get_misses);
- APPEND_STAT("delete_misses", "%llu", (unsigned long long)thread_stats.delete_misses);
- APPEND_STAT("delete_hits", "%llu", (unsigned long long)slab_stats.delete_hits);
- APPEND_STAT("incr_misses", "%llu", (unsigned long long)thread_stats.incr_misses);
- APPEND_STAT("incr_hits", "%llu", (unsigned long long)slab_stats.incr_hits);
- APPEND_STAT("decr_misses", "%llu", (unsigned long long)thread_stats.decr_misses);
- APPEND_STAT("decr_hits", "%llu", (unsigned long long)slab_stats.decr_hits);
- APPEND_STAT("cas_misses", "%llu", (unsigned long long)thread_stats.cas_misses);
- APPEND_STAT("cas_hits", "%llu", (unsigned long long)slab_stats.cas_hits);
- APPEND_STAT("cas_badval", "%llu", (unsigned long long)slab_stats.cas_badval);
- APPEND_STAT("touch_hits", "%llu", (unsigned long long)slab_stats.touch_hits);
- APPEND_STAT("touch_misses", "%llu", (unsigned long long)thread_stats.touch_misses);
- APPEND_STAT("auth_cmds", "%llu", (unsigned long long)thread_stats.auth_cmds);
- APPEND_STAT("auth_errors", "%llu", (unsigned long long)thread_stats.auth_errors);
- APPEND_STAT("bytes_read", "%llu", (unsigned long long)thread_stats.bytes_read);
- APPEND_STAT("bytes_written", "%llu", (unsigned long long)thread_stats.bytes_written);
- APPEND_STAT("limit_maxbytes", "%llu", (unsigned long long)settings.maxbytes);
- APPEND_STAT("accepting_conns", "%u", stats.accepting_conns);
- APPEND_STAT("listen_disabled_num", "%llu", (unsigned long long)stats.listen_disabled_num);
- APPEND_STAT("threads", "%d", settings.num_threads);
- APPEND_STAT("conn_yields", "%llu", (unsigned long long)thread_stats.conn_yields);
- APPEND_STAT("hash_power_level", "%u", stats.hash_power_level);
- APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats.hash_bytes);
- APPEND_STAT("hash_is_expanding", "%u", stats.hash_is_expanding);
- APPEND_STAT("expired_unfetched", "%llu", stats.expired_unfetched);
- APPEND_STAT("evicted_unfetched", "%llu", stats.evicted_unfetched);
- if (settings.slab_reassign) {
- APPEND_STAT("slab_reassign_running", "%u", stats.slab_reassign_running);
- APPEND_STAT("slabs_moved", "%llu", stats.slabs_moved);
- }
- STATS_UNLOCK();
-}
-
-static void process_stat_settings(ADD_STAT add_stats, void *c) {
- assert(add_stats);
- APPEND_STAT("maxbytes", "%u", (unsigned int)settings.maxbytes);
- APPEND_STAT("maxconns", "%d", settings.maxconns);
- APPEND_STAT("tcpport", "%d", settings.port);
- APPEND_STAT("udpport", "%d", settings.udpport);
- APPEND_STAT("inter", "%s", settings.inter ? settings.inter : "NULL");
- APPEND_STAT("verbosity", "%d", settings.verbose);
- APPEND_STAT("oldest", "%lu", (unsigned long)settings.oldest_live);
- APPEND_STAT("evictions", "%s", settings.evict_to_free ? "on" : "off");
- APPEND_STAT("domain_socket", "%s",
- settings.socketpath ? settings.socketpath : "NULL");
- APPEND_STAT("umask", "%o", settings.access);
- APPEND_STAT("growth_factor", "%.2f", settings.factor);
- APPEND_STAT("chunk_size", "%d", settings.chunk_size);
- APPEND_STAT("num_threads", "%d", settings.num_threads);
- APPEND_STAT("num_threads_per_udp", "%d", settings.num_threads_per_udp);
- APPEND_STAT("stat_key_prefix", "%c", settings.prefix_delimiter);
- APPEND_STAT("detail_enabled", "%s",
- settings.detail_enabled ? "yes" : "no");
- APPEND_STAT("reqs_per_event", "%d", settings.reqs_per_event);
- APPEND_STAT("cas_enabled", "%s", settings.use_cas ? "yes" : "no");
- APPEND_STAT("tcp_backlog", "%d", settings.backlog);
- APPEND_STAT("binding_protocol", "%s",
- prot_text(settings.binding_protocol));
- APPEND_STAT("auth_enabled_sasl", "%s", settings.sasl ? "yes" : "no");
- APPEND_STAT("item_size_max", "%d", settings.item_size_max);
- APPEND_STAT("maxconns_fast", "%s", settings.maxconns_fast ? "yes" : "no");
- APPEND_STAT("hashpower_init", "%d", settings.hashpower_init);
- APPEND_STAT("slab_reassign", "%s", settings.slab_reassign ? "yes" : "no");
- APPEND_STAT("slab_automove", "%d", settings.slab_automove);
-}
-
-static void process_stat(conn *c, token_t *tokens, const size_t ntokens) {
- const char *subcommand = tokens[SUBCOMMAND_TOKEN].value;
- assert(c != NULL);
-
- if (ntokens < 2) {
- out_string(c, "CLIENT_ERROR bad command line");
- return;
- }
-
- if (ntokens == 2) {
- server_stats(&append_stats, c);
- (void)get_stats(NULL, 0, &append_stats, c);
- } else if (strcmp(subcommand, "reset") == 0) {
- stats_reset();
- out_string(c, "RESET");
- return ;
- } else if (strcmp(subcommand, "detail") == 0) {
- /* NOTE: how to tackle detail with binary? */
- if (ntokens < 4)
- process_stats_detail(c, ""); /* outputs the error message */
- else
- process_stats_detail(c, tokens[2].value);
- /* Output already generated */
- return ;
- } else if (strcmp(subcommand, "settings") == 0) {
- process_stat_settings(&append_stats, c);
- } else if (strcmp(subcommand, "cachedump") == 0) {
- char *buf;
- unsigned int bytes, id, limit = 0;
-
- if (ntokens < 5) {
- out_string(c, "CLIENT_ERROR bad command line");
- return;
- }
-
- if (!safe_strtoul(tokens[2].value, &id) ||
- !safe_strtoul(tokens[3].value, &limit)) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- if (id >= POWER_LARGEST) {
- out_string(c, "CLIENT_ERROR Illegal slab id");
- return;
- }
-
- buf = item_cachedump(id, limit, &bytes);
- write_and_free(c, buf, bytes);
- return ;
- } else {
- /* getting here means that the subcommand is either engine specific or
- is invalid. query the engine and see. */
- if (get_stats(subcommand, strlen(subcommand), &append_stats, c)) {
- if (c->stats.buffer == NULL) {
- out_string(c, "SERVER_ERROR out of memory writing stats");
- } else {
- write_and_free(c, c->stats.buffer, c->stats.offset);
- c->stats.buffer = NULL;
- }
- } else {
- out_string(c, "ERROR");
- }
- return ;
- }
-
- /* append terminator and start the transfer */
- append_stats(NULL, 0, NULL, 0, c);
-
- if (c->stats.buffer == NULL) {
- out_string(c, "SERVER_ERROR out of memory writing stats");
- } else {
- write_and_free(c, c->stats.buffer, c->stats.offset);
- c->stats.buffer = NULL;
- }
-}
-
-/* ntokens is overwritten here... shrug.. */
-static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {
- char *key;
- size_t nkey;
- int i = 0;
- item *it;
- token_t *key_token = &tokens[KEY_TOKEN];
- char *suffix;
- assert(c != NULL);
-
- do {
- while(key_token->length != 0) {
-
- key = key_token->value;
- nkey = key_token->length;
-
- if(nkey > KEY_MAX_LENGTH) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- it = item_get(key, nkey);
- if (settings.detail_enabled) {
- stats_prefix_record_get(key, nkey, NULL != it);
- }
- if (it) {
- if (i >= c->isize) {
- item **new_list = realloc(c->ilist, sizeof(item *) * c->isize * 2);
- if (new_list) {
- c->isize *= 2;
- c->ilist = new_list;
- } else {
- item_remove(it);
- break;
- }
- }
-
- /*
- * Construct the response. Each hit adds three elements to the
- * outgoing data list:
- * "VALUE "
- * key
- * " " + flags + " " + data length + "\r\n" + data (with \r\n)
- */
-
- if (return_cas)
- {
- MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
- it->nbytes, ITEM_get_cas(it));
- /* Goofy mid-flight realloc. */
- if (i >= c->suffixsize) {
- char **new_suffix_list = realloc(c->suffixlist,
- sizeof(char *) * c->suffixsize * 2);
- if (new_suffix_list) {
- c->suffixsize *= 2;
- c->suffixlist = new_suffix_list;
- } else {
- item_remove(it);
- break;
- }
- }
-
- suffix = cache_alloc(c->thread->suffix_cache);
- if (suffix == NULL) {
- out_string(c, "SERVER_ERROR out of memory making CAS suffix");
- item_remove(it);
- return;
- }
- *(c->suffixlist + i) = suffix;
- int suffix_len = snprintf(suffix, SUFFIX_SIZE,
- " %llu\r\n",
- (unsigned long long)ITEM_get_cas(it));
- if (add_iov(c, "VALUE ", 6) != 0 ||
- add_iov(c, ITEM_key(it), it->nkey) != 0 ||
- add_iov(c, ITEM_suffix(it), it->nsuffix - 2) != 0 ||
- add_iov(c, suffix, suffix_len) != 0 ||
- add_iov(c, ITEM_data(it), it->nbytes) != 0)
- {
- item_remove(it);
- break;
- }
- }
- else
- {
- MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
- it->nbytes, ITEM_get_cas(it));
- if (add_iov(c, "VALUE ", 6) != 0 ||
- add_iov(c, ITEM_key(it), it->nkey) != 0 ||
- add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0)
- {
- item_remove(it);
- break;
- }
- }
-
-
- if (settings.verbose > 1)
- fprintf(stderr, ">%d sending key %s\n", c->sfd, ITEM_key(it));
-
- /* item_get() has incremented it->refcount for us */
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[it->slabs_clsid].get_hits++;
- c->thread->stats.get_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- item_update(it);
- *(c->ilist + i) = it;
- i++;
-
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.get_misses++;
- c->thread->stats.get_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
- }
-
- key_token++;
- }
-
- /*
- * If the command string hasn't been fully processed, get the next set
- * of tokens.
- */
- if(key_token->value != NULL) {
- ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
- key_token = tokens;
- }
-
- } while(key_token->value != NULL);
-
- c->icurr = c->ilist;
- c->ileft = i;
- if (return_cas) {
- c->suffixcurr = c->suffixlist;
- c->suffixleft = i;
- }
-
- if (settings.verbose > 1)
- fprintf(stderr, ">%d END\n", c->sfd);
-
- /*
- If the loop was terminated because of out-of-memory, it is not
- reliable to add END\r\n to the buffer, because it might not end
- in \r\n. So we send SERVER_ERROR instead.
- */
- if (key_token->value != NULL || add_iov(c, "END\r\n", 5) != 0
- || (IS_UDP(c->transport) && build_udp_headers(c) != 0)) {
- out_string(c, "SERVER_ERROR out of memory writing get response");
- }
- else {
- conn_set_state(c, conn_mwrite);
- c->msgcurr = 0;
- }
-
- return;
-}
-
-static void process_update_command(conn *c, token_t *tokens, const size_t ntokens, int comm, bool handle_cas) {
- char *key;
- size_t nkey;
- unsigned int flags;
- int32_t exptime_int = 0;
- time_t exptime;
- int vlen;
- uint64_t req_cas_id=0;
- item *it;
-
- assert(c != NULL);
-
- set_noreply_maybe(c, tokens, ntokens);
-
- if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- key = tokens[KEY_TOKEN].value;
- nkey = tokens[KEY_TOKEN].length;
-
- if (! (safe_strtoul(tokens[2].value, (uint32_t *)&flags)
- && safe_strtol(tokens[3].value, &exptime_int)
- && safe_strtol(tokens[4].value, (int32_t *)&vlen))) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- /* Ubuntu 8.04 breaks when I pass exptime to safe_strtol */
- exptime = exptime_int;
-
- /* Negative exptimes can underflow and end up immortal. realtime() will
- immediately expire values that are greater than REALTIME_MAXDELTA, but less
- than process_started, so lets aim for that. */
- if (exptime < 0)
- exptime = REALTIME_MAXDELTA + 1;
-
- // does cas value exist?
- if (handle_cas) {
- if (!safe_strtoull(tokens[5].value, &req_cas_id)) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
- }
-
- vlen += 2;
- if (vlen < 0 || vlen - 2 < 0) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_set(key, nkey);
- }
-
- it = item_alloc(key, nkey, flags, realtime(exptime), vlen);
-
- if (it == 0) {
- if (! item_size_ok(nkey, flags, vlen))
- out_string(c, "SERVER_ERROR object too large for cache");
- else
- out_string(c, "SERVER_ERROR out of memory storing object");
- /* swallow the data line */
- c->write_and_go = conn_swallow;
- c->sbytes = vlen;
-
- /* Avoid stale data persisting in cache because we failed alloc.
- * Unacceptable for SET. Anywhere else too? */
- if (comm == NREAD_SET) {
- it = item_get(key, nkey);
- if (it) {
- item_unlink(it);
- item_remove(it);
- }
- }
-
- return;
- }
- ITEM_set_cas(it, req_cas_id);
-
- c->item = it;
- c->ritem = ITEM_data(it);
- c->rlbytes = it->nbytes;
- c->cmd = comm;
- conn_set_state(c, conn_nread);
-}
-
-static void process_touch_command(conn *c, token_t *tokens, const size_t ntokens) {
- char *key;
- size_t nkey;
- int32_t exptime_int = 0;
- item *it;
-
- assert(c != NULL);
-
- set_noreply_maybe(c, tokens, ntokens);
-
- if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- key = tokens[KEY_TOKEN].value;
- nkey = tokens[KEY_TOKEN].length;
-
- if (!safe_strtol(tokens[2].value, &exptime_int)) {
- out_string(c, "CLIENT_ERROR invalid exptime argument");
- return;
- }
-
- it = item_touch(key, nkey, realtime(exptime_int));
- if (it) {
- item_update(it);
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.touch_cmds++;
- c->thread->stats.slab_stats[it->slabs_clsid].touch_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- out_string(c, "TOUCHED");
- item_remove(it);
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.touch_cmds++;
- c->thread->stats.touch_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- out_string(c, "NOT_FOUND");
- }
-}
-
-static void process_arithmetic_command(conn *c, token_t *tokens, const size_t ntokens, const bool incr) {
- char temp[INCR_MAX_STORAGE_LEN];
- uint64_t delta;
- char *key;
- size_t nkey;
-
- assert(c != NULL);
-
- set_noreply_maybe(c, tokens, ntokens);
-
- if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- key = tokens[KEY_TOKEN].value;
- nkey = tokens[KEY_TOKEN].length;
-
- if (!safe_strtoull(tokens[2].value, &delta)) {
- out_string(c, "CLIENT_ERROR invalid numeric delta argument");
- return;
- }
-
- switch(add_delta(c, key, nkey, incr, delta, temp, NULL)) {
- case OK:
- out_string(c, temp);
- break;
- case NON_NUMERIC:
- out_string(c, "CLIENT_ERROR cannot increment or decrement non-numeric value");
- break;
- case EOM:
- out_string(c, "SERVER_ERROR out of memory");
- break;
- case DELTA_ITEM_NOT_FOUND:
- pthread_mutex_lock(&c->thread->stats.mutex);
- if (incr) {
- c->thread->stats.incr_misses++;
- } else {
- c->thread->stats.decr_misses++;
- }
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- out_string(c, "NOT_FOUND");
- break;
- case DELTA_ITEM_CAS_MISMATCH:
- break; /* Should never get here */
- }
-}
-
-/*
- * adds a delta value to a numeric item.
- *
- * c connection requesting the operation
- * it item to adjust
- * incr true to increment value, false to decrement
- * delta amount to adjust value by
- * buf buffer for response string
- *
- * returns a response string to send back to the client.
- */
-enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey,
- const bool incr, const int64_t delta,
- char *buf, uint64_t *cas,
- const uint32_t hv) {
- char *ptr;
- uint64_t value;
- int res;
- item *it;
-
- it = do_item_get(key, nkey, hv);
- if (!it) {
- return DELTA_ITEM_NOT_FOUND;
- }
-
- if (cas != NULL && *cas != 0 && ITEM_get_cas(it) != *cas) {
- do_item_remove(it);
- return DELTA_ITEM_CAS_MISMATCH;
- }
-
- ptr = ITEM_data(it);
-
- if (!safe_strtoull(ptr, &value)) {
- do_item_remove(it);
- return NON_NUMERIC;
- }
-
- if (incr) {
- value += delta;
- MEMCACHED_COMMAND_INCR(c->sfd, ITEM_key(it), it->nkey, value);
- } else {
- if(delta > value) {
- value = 0;
- } else {
- value -= delta;
- }
- MEMCACHED_COMMAND_DECR(c->sfd, ITEM_key(it), it->nkey, value);
- }
-
- pthread_mutex_lock(&c->thread->stats.mutex);
- if (incr) {
- c->thread->stats.slab_stats[it->slabs_clsid].incr_hits++;
- } else {
- c->thread->stats.slab_stats[it->slabs_clsid].decr_hits++;
- }
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- snprintf(buf, INCR_MAX_STORAGE_LEN, "%llu", (unsigned long long)value);
- res = strlen(buf);
- if (res + 2 > it->nbytes || it->refcount != 1) { /* need to realloc */
- item *new_it;
- new_it = item_alloc(ITEM_key(it), it->nkey, atoi(ITEM_suffix(it) + 1), it->exptime, res + 2 );
- if (new_it == 0) {
- do_item_remove(it);
- return EOM;
- }
- memcpy(ITEM_data(new_it), buf, res);
- memcpy(ITEM_data(new_it) + res, "\r\n", 2);
- item_replace(it, new_it, hv);
- // Overwrite the older item's CAS with our new CAS since we're
- // returning the CAS of the old item below.
- ITEM_set_cas(it, (settings.use_cas) ? ITEM_get_cas(new_it) : 0);
- do_item_remove(new_it); /* release our reference */
- } else { /* replace in-place */
- /* When changing the value without replacing the item, we
- need to update the CAS on the existing item. */
- mutex_lock(&cache_lock); /* FIXME */
- ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
- mutex_unlock(&cache_lock);
-
- memcpy(ITEM_data(it), buf, res);
- memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
- do_item_update(it);
- }
-
- if (cas) {
- *cas = ITEM_get_cas(it); /* swap the incoming CAS value */
- }
- do_item_remove(it); /* release our reference */
- return OK;
-}
-
-static void process_delete_command(conn *c, token_t *tokens, const size_t ntokens) {
- char *key;
- size_t nkey;
- item *it;
-
- assert(c != NULL);
-
- if (ntokens > 3) {
- bool hold_is_zero = strcmp(tokens[KEY_TOKEN+1].value, "0") == 0;
- bool sets_noreply = set_noreply_maybe(c, tokens, ntokens);
- bool valid = (ntokens == 4 && (hold_is_zero || sets_noreply))
- || (ntokens == 5 && hold_is_zero && sets_noreply);
- if (!valid) {
- out_string(c, "CLIENT_ERROR bad command line format. "
- "Usage: delete <key> [noreply]");
- return;
- }
- }
-
-
- key = tokens[KEY_TOKEN].value;
- nkey = tokens[KEY_TOKEN].length;
-
- if(nkey > KEY_MAX_LENGTH) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- if (settings.detail_enabled) {
- stats_prefix_record_delete(key, nkey);
- }
-
- it = item_get(key, nkey);
- if (it) {
- MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
-
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.slab_stats[it->slabs_clsid].delete_hits++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- item_unlink(it);
- item_remove(it); /* release our reference */
- out_string(c, "DELETED");
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.delete_misses++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- out_string(c, "NOT_FOUND");
- }
-}
-
-static void process_verbosity_command(conn *c, token_t *tokens, const size_t ntokens) {
- unsigned int level;
-
- assert(c != NULL);
-
- set_noreply_maybe(c, tokens, ntokens);
-
- level = strtoul(tokens[1].value, NULL, 10);
- settings.verbose = level > MAX_VERBOSITY_LEVEL ? MAX_VERBOSITY_LEVEL : level;
- out_string(c, "OK");
- return;
-}
-
-static void process_slabs_automove_command(conn *c, token_t *tokens, const size_t ntokens) {
- unsigned int level;
-
- assert(c != NULL);
-
- set_noreply_maybe(c, tokens, ntokens);
-
- level = strtoul(tokens[2].value, NULL, 10);
- if (level == 0) {
- settings.slab_automove = 0;
- } else if (level == 1 || level == 2) {
- settings.slab_automove = level;
- } else {
- out_string(c, "ERROR");
- return;
- }
- out_string(c, "OK");
- return;
-}
-
-static void process_command(conn *c, char *command) {
-
- token_t tokens[MAX_TOKENS];
- size_t ntokens;
- int comm;
-
- assert(c != NULL);
-
- MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
-
- if (settings.verbose > 1)
- fprintf(stderr, "<%d %s\n", c->sfd, command);
-
- /*
- * for commands set/add/replace, we build an item and read the data
- * directly into it, then continue in nread_complete().
- */
-
- c->msgcurr = 0;
- c->msgused = 0;
- c->iovused = 0;
- if (add_msghdr(c) != 0) {
- out_string(c, "SERVER_ERROR out of memory preparing response");
- return;
- }
-
- ntokens = tokenize_command(command, tokens, MAX_TOKENS);
- if (ntokens >= 3 &&
- ((strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) ||
- (strcmp(tokens[COMMAND_TOKEN].value, "bget") == 0))) {
-
- process_get_command(c, tokens, ntokens, false);
-
- } else if ((ntokens == 6 || ntokens == 7) &&
- ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) ||
- (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) ||
- (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)) ||
- (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) ||
- (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) )) {
-
- process_update_command(c, tokens, ntokens, comm, false);
-
- } else if ((ntokens == 7 || ntokens == 8) && (strcmp(tokens[COMMAND_TOKEN].value, "cas") == 0 && (comm = NREAD_CAS))) {
-
- process_update_command(c, tokens, ntokens, comm, true);
-
- } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) {
-
- process_arithmetic_command(c, tokens, ntokens, 1);
-
- } else if (ntokens >= 3 && (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0)) {
-
- process_get_command(c, tokens, ntokens, true);
-
- } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "decr") == 0)) {
-
- process_arithmetic_command(c, tokens, ntokens, 0);
-
- } else if (ntokens >= 3 && ntokens <= 5 && (strcmp(tokens[COMMAND_TOKEN].value, "delete") == 0)) {
-
- process_delete_command(c, tokens, ntokens);
-
- } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "touch") == 0)) {
-
- process_touch_command(c, tokens, ntokens);
-
- } else if (ntokens >= 2 && (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0)) {
-
- process_stat(c, tokens, ntokens);
-
- } else if (ntokens >= 2 && ntokens <= 4 && (strcmp(tokens[COMMAND_TOKEN].value, "flush_all") == 0)) {
- time_t exptime = 0;
-
- set_noreply_maybe(c, tokens, ntokens);
-
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.flush_cmds++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- if(ntokens == (c->noreply ? 3 : 2)) {
- settings.oldest_live = current_time - 1;
- item_flush_expired();
- out_string(c, "OK");
- return;
- }
-
- exptime = strtol(tokens[1].value, NULL, 10);
- if(errno == ERANGE) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- /*
- If exptime is zero realtime() would return zero too, and
- realtime(exptime) - 1 would overflow to the max unsigned
- value. So we process exptime == 0 the same way we do when
- no delay is given at all.
- */
- if (exptime > 0)
- settings.oldest_live = realtime(exptime) - 1;
- else /* exptime == 0 */
- settings.oldest_live = current_time - 1;
- item_flush_expired();
- out_string(c, "OK");
- return;
-
- } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "version") == 0)) {
-
- out_string(c, "VERSION " VERSION);
-
- } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "quit") == 0)) {
-
- conn_set_state(c, conn_closing);
-
- } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "slabs") == 0) {
- if (ntokens == 5 && strcmp(tokens[COMMAND_TOKEN + 1].value, "reassign") == 0) {
- int src, dst, rv;
-
- if (settings.slab_reassign == false) {
- out_string(c, "CLIENT_ERROR slab reassignment disabled");
- return;
- }
-
- src = strtol(tokens[2].value, NULL, 10);
- dst = strtol(tokens[3].value, NULL, 10);
-
- if (errno == ERANGE) {
- out_string(c, "CLIENT_ERROR bad command line format");
- return;
- }
-
- rv = slabs_reassign(src, dst);
- switch (rv) {
- case REASSIGN_OK:
- out_string(c, "OK");
- break;
- case REASSIGN_RUNNING:
- out_string(c, "BUSY currently processing reassign request");
- break;
- case REASSIGN_BADCLASS:
- out_string(c, "BADCLASS invalid src or dst class id");
- break;
- case REASSIGN_NOSPARE:
- out_string(c, "NOSPARE source class has no spare pages");
- break;
- case REASSIGN_SRC_DST_SAME:
- out_string(c, "SAME src and dst class are identical");
- break;
- }
- return;
- } else if (ntokens == 4 &&
- (strcmp(tokens[COMMAND_TOKEN + 1].value, "automove") == 0)) {
- process_slabs_automove_command(c, tokens, ntokens);
- } else {
- out_string(c, "ERROR");
- }
- } else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "verbosity") == 0)) {
- process_verbosity_command(c, tokens, ntokens);
- } else {
- out_string(c, "ERROR");
- }
- return;
-}
-
-/*
- * if we have a complete line in the buffer, process it.
- */
-static int try_read_command(conn *c) {
- assert(c != NULL);
- assert(c->rcurr <= (c->rbuf + c->rsize));
- assert(c->rbytes > 0);
-
- if (c->protocol == negotiating_prot || c->transport == udp_transport) {
- if ((unsigned char)c->rbuf[0] == (unsigned char)PROTOCOL_BINARY_REQ) {
- c->protocol = binary_prot;
- } else {
- c->protocol = ascii_prot;
- }
-
- if (settings.verbose > 1) {
- fprintf(stderr, "%d: Client using the %s protocol\n", c->sfd,
- prot_text(c->protocol));
- }
- }
-
- if (c->protocol == binary_prot) {
- /* Do we have the complete packet header? */
- if (c->rbytes < sizeof(c->binary_header)) {
- /* need more data! */
- return 0;
- } else {
-#ifdef NEED_ALIGN
- if (((long)(c->rcurr)) % 8 != 0) {
- /* must realign input buffer */
- memmove(c->rbuf, c->rcurr, c->rbytes);
- c->rcurr = c->rbuf;
- if (settings.verbose > 1) {
- fprintf(stderr, "%d: Realign input buffer\n", c->sfd);
- }
- }
-#endif
- protocol_binary_request_header* req;
- req = (protocol_binary_request_header*)c->rcurr;
-
- if (settings.verbose > 1) {
- /* Dump the packet before we convert it to host order */
- int ii;
- fprintf(stderr, "<%d Read binary protocol data:", c->sfd);
- for (ii = 0; ii < sizeof(req->bytes); ++ii) {
- if (ii % 4 == 0) {
- fprintf(stderr, "\n<%d ", c->sfd);
- }
- fprintf(stderr, " 0x%02x", req->bytes[ii]);
- }
- fprintf(stderr, "\n");
- }
-
- c->binary_header = *req;
- c->binary_header.request.keylen = ntohs(req->request.keylen);
- c->binary_header.request.bodylen = ntohl(req->request.bodylen);
- c->binary_header.request.cas = ntohll(req->request.cas);
-
- if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ) {
- if (settings.verbose) {
- fprintf(stderr, "Invalid magic: %x\n",
- c->binary_header.request.magic);
- }
- conn_set_state(c, conn_closing);
- return -1;
- }
-
- c->msgcurr = 0;
- c->msgused = 0;
- c->iovused = 0;
- if (add_msghdr(c) != 0) {
- out_string(c, "SERVER_ERROR out of memory");
- return 0;
- }
-
- c->cmd = c->binary_header.request.opcode;
- c->keylen = c->binary_header.request.keylen;
- c->opaque = c->binary_header.request.opaque;
- /* clear the returned cas value */
- c->cas = 0;
-
- dispatch_bin_command(c);
-
- c->rbytes -= sizeof(c->binary_header);
- c->rcurr += sizeof(c->binary_header);
- }
- } else {
- char *el, *cont;
-
- if (c->rbytes == 0)
- return 0;
-
- el = memchr(c->rcurr, '\n', c->rbytes);
- if (!el) {
- if (c->rbytes > 1024) {
- /*
- * We didn't have a '\n' in the first k. This _has_ to be a
- * large multiget, if not we should just nuke the connection.
- */
- char *ptr = c->rcurr;
- while (*ptr == ' ') { /* ignore leading whitespaces */
- ++ptr;
- }
-
- if (ptr - c->rcurr > 100 ||
- (strncmp(ptr, "get ", 4) && strncmp(ptr, "gets ", 5))) {
-
- conn_set_state(c, conn_closing);
- return 1;
- }
- }
-
- return 0;
- }
- cont = el + 1;
- if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
- el--;
- }
- *el = '\0';
-
- assert(cont <= (c->rcurr + c->rbytes));
-
- process_command(c, c->rcurr);
-
- c->rbytes -= (cont - c->rcurr);
- c->rcurr = cont;
-
- assert(c->rcurr <= (c->rbuf + c->rsize));
- }
-
- return 1;
-}
-
-/*
- * read a UDP request.
- */
-static enum try_read_result try_read_udp(conn *c) {
- int res;
-
- assert(c != NULL);
-
- c->request_addr_size = sizeof(c->request_addr);
- res = recvfrom(c->sfd, c->rbuf, c->rsize,
- 0, &c->request_addr, &c->request_addr_size);
- if (res > 8) {
- unsigned char *buf = (unsigned char *)c->rbuf;
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.bytes_read += res;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- /* Beginning of UDP packet is the request ID; save it. */
- c->request_id = buf[0] * 256 + buf[1];
-
- /* If this is a multi-packet request, drop it. */
- if (buf[4] != 0 || buf[5] != 1) {
- out_string(c, "SERVER_ERROR multi-packet request not supported");
- return READ_NO_DATA_RECEIVED;
- }
-
- /* Don't care about any of the rest of the header. */
- res -= 8;
- memmove(c->rbuf, c->rbuf + 8, res);
-
- c->rbytes = res;
- c->rcurr = c->rbuf;
- return READ_DATA_RECEIVED;
- }
- return READ_NO_DATA_RECEIVED;
-}
-
-/*
- * read from network as much as we can, handle buffer overflow and connection
- * close.
- * before reading, move the remaining incomplete fragment of a command
- * (if any) to the beginning of the buffer.
- *
- * To protect us from someone flooding a connection with bogus data causing
- * the connection to eat up all available memory, break out and start looking
- * at the data I've got after a number of reallocs...
- *
- * @return enum try_read_result
- */
-static enum try_read_result try_read_network(conn *c) {
- enum try_read_result gotdata = READ_NO_DATA_RECEIVED;
- int res;
- int num_allocs = 0;
- assert(c != NULL);
-
- if (c->rcurr != c->rbuf) {
- if (c->rbytes != 0) /* otherwise there's nothing to copy */
- memmove(c->rbuf, c->rcurr, c->rbytes);
- c->rcurr = c->rbuf;
- }
-
- while (1) {
- if (c->rbytes >= c->rsize) {
- if (num_allocs == 4) {
- return gotdata;
- }
- ++num_allocs;
- char *new_rbuf = realloc(c->rbuf, c->rsize * 2);
- if (!new_rbuf) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't realloc input buffer\n");
- c->rbytes = 0; /* ignore what we read */
- out_string(c, "SERVER_ERROR out of memory reading request");
- c->write_and_go = conn_closing;
- return READ_MEMORY_ERROR;
- }
- c->rcurr = c->rbuf = new_rbuf;
- c->rsize *= 2;
- }
-
- int avail = c->rsize - c->rbytes;
- res = read(c->sfd, c->rbuf + c->rbytes, avail);
- if (res > 0) {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.bytes_read += res;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- gotdata = READ_DATA_RECEIVED;
- c->rbytes += res;
- if (res == avail) {
- continue;
- } else {
- break;
- }
- }
- if (res == 0) {
- return READ_ERROR;
- }
- if (res == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- break;
- }
- return READ_ERROR;
- }
- }
- return gotdata;
-}
-
-static bool update_event(conn *c, const int new_flags) {
- assert(c != NULL);
-
- struct event_base *base = c->event.ev_base;
- if (c->ev_flags == new_flags)
- return true;
- if (event_del(&c->event) == -1) return false;
- event_set(&c->event, c->sfd, new_flags, event_handler, (void *)c);
- event_base_set(base, &c->event);
- c->ev_flags = new_flags;
- if (event_add(&c->event, 0) == -1) return false;
- return true;
-}
-
-/*
- * Sets whether we are listening for new connections or not.
- */
-void do_accept_new_conns(const bool do_accept) {
- conn *next;
-
- for (next = listen_conn; next; next = next->next) {
- if (do_accept) {
- update_event(next, EV_READ | EV_PERSIST);
- if (listen(next->sfd, settings.backlog) != 0) {
- perror("listen");
- }
- }
- else {
- update_event(next, 0);
- if (listen(next->sfd, 0) != 0) {
- perror("listen");
- }
- }
- }
-
- if (do_accept) {
- STATS_LOCK();
- stats.accepting_conns = true;
- STATS_UNLOCK();
- } else {
- STATS_LOCK();
- stats.accepting_conns = false;
- stats.listen_disabled_num++;
- STATS_UNLOCK();
- allow_new_conns = false;
- maxconns_handler(-42, 0, 0);
- }
-}
-
-/*
- * Transmit the next chunk of data from our list of msgbuf structures.
- *
- * Returns:
- * TRANSMIT_COMPLETE All done writing.
- * TRANSMIT_INCOMPLETE More data remaining to write.
- * TRANSMIT_SOFT_ERROR Can't write any more right now.
- * TRANSMIT_HARD_ERROR Can't write (c->state is set to conn_closing)
- */
-static enum transmit_result transmit(conn *c) {
- assert(c != NULL);
-
- if (c->msgcurr < c->msgused &&
- c->msglist[c->msgcurr].msg_iovlen == 0) {
- /* Finished writing the current msg; advance to the next. */
- c->msgcurr++;
- }
- if (c->msgcurr < c->msgused) {
- ssize_t res;
- struct msghdr *m = &c->msglist[c->msgcurr];
-
- res = sendmsg(c->sfd, m, 0);
- if (res > 0) {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.bytes_written += res;
- pthread_mutex_unlock(&c->thread->stats.mutex);
-
- /* We've written some of the data. Remove the completed
- iovec entries from the list of pending writes. */
- while (m->msg_iovlen > 0 && res >= m->msg_iov->iov_len) {
- res -= m->msg_iov->iov_len;
- m->msg_iovlen--;
- m->msg_iov++;
- }
-
- /* Might have written just part of the last iovec entry;
- adjust it so the next write will do the rest. */
- if (res > 0) {
- m->msg_iov->iov_base = (caddr_t)m->msg_iov->iov_base + res;
- m->msg_iov->iov_len -= res;
- }
- return TRANSMIT_INCOMPLETE;
- }
- if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
- if (!update_event(c, EV_WRITE | EV_PERSIST)) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't update event\n");
- conn_set_state(c, conn_closing);
- return TRANSMIT_HARD_ERROR;
- }
- return TRANSMIT_SOFT_ERROR;
- }
- /* if res == 0 or res == -1 and error is not EAGAIN or EWOULDBLOCK,
- we have a real error, on which we close the connection */
- if (settings.verbose > 0)
- perror("Failed to write, and not due to blocking");
-
- if (IS_UDP(c->transport))
- conn_set_state(c, conn_read);
- else
- conn_set_state(c, conn_closing);
- return TRANSMIT_HARD_ERROR;
- } else {
- return TRANSMIT_COMPLETE;
- }
-}
-
-static void drive_machine(conn *c) {
- bool stop = false;
- int sfd, flags = 1;
- socklen_t addrlen;
- struct sockaddr_storage addr;
- int nreqs = settings.reqs_per_event;
- int res;
- const char *str;
-
- assert(c != NULL);
-
- while (!stop) {
-
- switch(c->state) {
- case conn_listening:
- addrlen = sizeof(addr);
- if ((sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen)) == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- /* these are transient, so don't log anything */
- stop = true;
- } else if (errno == EMFILE) {
- if (settings.verbose > 0)
- fprintf(stderr, "Too many open connections\n");
- accept_new_conns(false);
- stop = true;
- } else {
- perror("accept()");
- stop = true;
- }
- break;
- }
- if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
- fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
- perror("setting O_NONBLOCK");
- close(sfd);
- break;
- }
-
- if (settings.maxconns_fast &&
- stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {
- str = "ERROR Too many open connections\r\n";
- res = write(sfd, str, strlen(str));
- close(sfd);
- STATS_LOCK();
- stats.rejected_conns++;
- STATS_UNLOCK();
- } else {
- dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,
- DATA_BUFFER_SIZE, tcp_transport);
- }
-
- stop = true;
- break;
-
- case conn_waiting:
- if (!update_event(c, EV_READ | EV_PERSIST)) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't update event\n");
- conn_set_state(c, conn_closing);
- break;
- }
-
- conn_set_state(c, conn_read);
- stop = true;
- break;
-
- case conn_read:
- res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);
-
- switch (res) {
- case READ_NO_DATA_RECEIVED:
- conn_set_state(c, conn_waiting);
- break;
- case READ_DATA_RECEIVED:
- conn_set_state(c, conn_parse_cmd);
- break;
- case READ_ERROR:
- conn_set_state(c, conn_closing);
- break;
- case READ_MEMORY_ERROR: /* Failed to allocate more memory */
- /* State already set by try_read_network */
- break;
- }
- break;
-
- case conn_parse_cmd :
- if (try_read_command(c) == 0) {
- /* wee need more data! */
- conn_set_state(c, conn_waiting);
- }
-
- break;
-
- case conn_new_cmd:
- /* Only process nreqs at a time to avoid starving other
- connections */
-
- --nreqs;
- if (nreqs >= 0) {
- reset_cmd_handler(c);
- } else {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.conn_yields++;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- if (c->rbytes > 0) {
- /* We have already read in data into the input buffer,
- so libevent will most likely not signal read events
- on the socket (unless more data is available. As a
- hack we should just put in a request to write data,
- because that should be possible ;-)
- */
- if (!update_event(c, EV_WRITE | EV_PERSIST)) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't update event\n");
- conn_set_state(c, conn_closing);
- }
- }
- stop = true;
- }
- break;
-
- case conn_nread:
- if (c->rlbytes == 0) {
- complete_nread(c);
- break;
- }
- /* first check if we have leftovers in the conn_read buffer */
- if (c->rbytes > 0) {
- int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
- if (c->ritem != c->rcurr) {
- memmove(c->ritem, c->rcurr, tocopy);
- }
- c->ritem += tocopy;
- c->rlbytes -= tocopy;
- c->rcurr += tocopy;
- c->rbytes -= tocopy;
- if (c->rlbytes == 0) {
- break;
- }
- }
-
- /* now try reading from the socket */
- res = read(c->sfd, c->ritem, c->rlbytes);
- if (res > 0) {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.bytes_read += res;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- if (c->rcurr == c->ritem) {
- c->rcurr += res;
- }
- c->ritem += res;
- c->rlbytes -= res;
- break;
- }
- if (res == 0) { /* end of stream */
- conn_set_state(c, conn_closing);
- break;
- }
- if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
- if (!update_event(c, EV_READ | EV_PERSIST)) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't update event\n");
- conn_set_state(c, conn_closing);
- break;
- }
- stop = true;
- break;
- }
- /* otherwise we have a real error, on which we close the connection */
- if (settings.verbose > 0) {
- fprintf(stderr, "Failed to read, and not due to blocking:\n"
- "errno: %d %s \n"
- "rcurr=%lx ritem=%lx rbuf=%lx rlbytes=%d rsize=%d\n",
- errno, strerror(errno),
- (long)c->rcurr, (long)c->ritem, (long)c->rbuf,
- (int)c->rlbytes, (int)c->rsize);
- }
- conn_set_state(c, conn_closing);
- break;
-
- case conn_swallow:
- /* we are reading sbytes and throwing them away */
- if (c->sbytes == 0) {
- conn_set_state(c, conn_new_cmd);
- break;
- }
-
- /* first check if we have leftovers in the conn_read buffer */
- if (c->rbytes > 0) {
- int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes;
- c->sbytes -= tocopy;
- c->rcurr += tocopy;
- c->rbytes -= tocopy;
- break;
- }
-
- /* now try reading from the socket */
- res = read(c->sfd, c->rbuf, c->rsize > c->sbytes ? c->sbytes : c->rsize);
- if (res > 0) {
- pthread_mutex_lock(&c->thread->stats.mutex);
- c->thread->stats.bytes_read += res;
- pthread_mutex_unlock(&c->thread->stats.mutex);
- c->sbytes -= res;
- break;
- }
- if (res == 0) { /* end of stream */
- conn_set_state(c, conn_closing);
- break;
- }
- if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
- if (!update_event(c, EV_READ | EV_PERSIST)) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't update event\n");
- conn_set_state(c, conn_closing);
- break;
- }
- stop = true;
- break;
- }
- /* otherwise we have a real error, on which we close the connection */
- if (settings.verbose > 0)
- fprintf(stderr, "Failed to read, and not due to blocking\n");
- conn_set_state(c, conn_closing);
- break;
-
- case conn_write:
- /*
- * We want to write out a simple response. If we haven't already,
- * assemble it into a msgbuf list (this will be a single-entry
- * list for TCP or a two-entry list for UDP).
- */
- if (c->iovused == 0 || (IS_UDP(c->transport) && c->iovused == 1)) {
- if (add_iov(c, c->wcurr, c->wbytes) != 0) {
- if (settings.verbose > 0)
- fprintf(stderr, "Couldn't build response\n");
- conn_set_state(c, conn_closing);
- break;
- }
- }
-
- /* fall through... */
-
- case conn_mwrite:
- if (IS_UDP(c->transport) && c->msgcurr == 0 && build_udp_headers(c) != 0) {
- if (settings.verbose > 0)
- fprintf(stderr, "Failed to build UDP headers\n");
- conn_set_state(c, conn_closing);
- break;
- }
- switch (transmit(c)) {
- case TRANSMIT_COMPLETE:
- if (c->state == conn_mwrite) {
- while (c->ileft > 0) {
- item *it = *(c->icurr);
- assert((it->it_flags & ITEM_SLABBED) == 0);
- item_remove(it);
- c->icurr++;
- c->ileft--;
- }
- while (c->suffixleft > 0) {
- char *suffix = *(c->suffixcurr);
- cache_free(c->thread->suffix_cache, suffix);
- c->suffixcurr++;
- c->suffixleft--;
- }
- /* XXX: I don't know why this wasn't the general case */
- if(c->protocol == binary_prot) {
- conn_set_state(c, c->write_and_go);
- } else {
- conn_set_state(c, conn_new_cmd);
- }
- } else if (c->state == conn_write) {
- if (c->write_and_free) {
- free(c->write_and_free);
- c->write_and_free = 0;
- }
- conn_set_state(c, c->write_and_go);
- } else {
- if (settings.verbose > 0)
- fprintf(stderr, "Unexpected state %d\n", c->state);
- conn_set_state(c, conn_closing);
- }
- break;
-
- case TRANSMIT_INCOMPLETE:
- case TRANSMIT_HARD_ERROR:
- break; /* Continue in state machine. */
-
- case TRANSMIT_SOFT_ERROR:
- stop = true;
- break;
- }
- break;
-
- case conn_closing:
- if (IS_UDP(c->transport))
- conn_cleanup(c);
- else
- conn_close(c);
- stop = true;
- break;
-
- case conn_max_state:
- assert(false);
- break;
- }
- }
-
- return;
-}
-
-void event_handler(const int fd, const short which, void *arg) {
- conn *c;
-
- c = (conn *)arg;
- assert(c != NULL);
-
- c->which = which;
-
- /* sanity */
- if (fd != c->sfd) {
- if (settings.verbose > 0)
- fprintf(stderr, "Catastrophic: event fd doesn't match conn fd!\n");
- conn_close(c);
- return;
- }
-
- drive_machine(c);
-
- /* wait for next event */
- return;
-}
-
-static int new_socket(struct addrinfo *ai) {
- int sfd;
- int flags;
-
- if ((sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
- return -1;
- }
-
- if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
- fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
- perror("setting O_NONBLOCK");
- close(sfd);
- return -1;
- }
- return sfd;
-}
-
-
-/*
- * Sets a socket's send buffer size to the maximum allowed by the system.
- */
-static void maximize_sndbuf(const int sfd) {
- socklen_t intsize = sizeof(int);
- int last_good = 0;
- int min, max, avg;
- int old_size;
-
- /* Start with the default size. */
- if (getsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &old_size, &intsize) != 0) {
- if (settings.verbose > 0)
- perror("getsockopt(SO_SNDBUF)");
- return;
- }
-
- /* Binary-search for the real maximum. */
- min = old_size;
- max = MAX_SENDBUF_SIZE;
-
- while (min <= max) {
- avg = ((unsigned int)(min + max)) / 2;
- if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void *)&avg, intsize) == 0) {
- last_good = avg;
- min = avg + 1;
- } else {
- max = avg - 1;
- }
- }
-
- if (settings.verbose > 1)
- fprintf(stderr, "<%d send buffer was %d, now %d\n", sfd, old_size, last_good);
-}
-
-/**
- * Create a socket and bind it to a specific port number
- * @param interface the interface to bind to
- * @param port the port number to bind to
- * @param transport the transport protocol (TCP / UDP)
- * @param portnumber_file A filepointer to write the port numbers to
- * when they are successfully added to the list of ports we
- * listen on.
- */
-static int server_socket(const char *interface,
- int port,
- enum network_transport transport,
- FILE *portnumber_file) {
- int sfd;
- struct linger ling = {0, 0};
- struct addrinfo *ai;
- struct addrinfo *next;
- struct addrinfo hints = { .ai_flags = AI_PASSIVE,
- .ai_family = AF_UNSPEC };
- char port_buf[NI_MAXSERV];
- int error;
- int success = 0;
- int flags =1;
-
- hints.ai_socktype = IS_UDP(transport) ? SOCK_DGRAM : SOCK_STREAM;
-
- if (port == -1) {
- port = 0;
- }
- snprintf(port_buf, sizeof(port_buf), "%d", port);
- error= getaddrinfo(interface, port_buf, &hints, &ai);
- if (error != 0) {
- if (error != EAI_SYSTEM)
- fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
- else
- perror("getaddrinfo()");
- return 1;
- }
-
- for (next= ai; next; next= next->ai_next) {
- conn *listen_conn_add;
- if ((sfd = new_socket(next)) == -1) {
- /* getaddrinfo can return "junk" addresses,
- * we make sure at least one works before erroring.
- */
- if (errno == EMFILE) {
- /* ...unless we're out of fds */
- perror("server_socket");
- exit(EX_OSERR);
- }
- continue;
- }
-
-#ifdef IPV6_V6ONLY
- if (next->ai_family == AF_INET6) {
- error = setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flags, sizeof(flags));
- if (error != 0) {
- perror("setsockopt");
- close(sfd);
- continue;
- }
- }
-#endif
-
- setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
- if (IS_UDP(transport)) {
- maximize_sndbuf(sfd);
- } else {
- error = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
- if (error != 0)
- perror("setsockopt");
-
- error = setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
- if (error != 0)
- perror("setsockopt");
-
- error = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
- if (error != 0)
- perror("setsockopt");
- }
-
- if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) {
- if (errno != EADDRINUSE) {
- perror("bind()");
- close(sfd);
- freeaddrinfo(ai);
- return 1;
- }
- close(sfd);
- continue;
- } else {
- success++;
- if (!IS_UDP(transport) && listen(sfd, settings.backlog) == -1) {
- perror("listen()");
- close(sfd);
- freeaddrinfo(ai);
- return 1;
- }
- if (portnumber_file != NULL &&
- (next->ai_addr->sa_family == AF_INET ||
- next->ai_addr->sa_family == AF_INET6)) {
- union {
- struct sockaddr_in in;
- struct sockaddr_in6 in6;
- } my_sockaddr;
- socklen_t len = sizeof(my_sockaddr);
- if (getsockname(sfd, (struct sockaddr*)&my_sockaddr, &len)==0) {
- if (next->ai_addr->sa_family == AF_INET) {
- fprintf(portnumber_file, "%s INET: %u\n",
- IS_UDP(transport) ? "UDP" : "TCP",
- ntohs(my_sockaddr.in.sin_port));
- } else {
- fprintf(portnumber_file, "%s INET6: %u\n",
- IS_UDP(transport) ? "UDP" : "TCP",
- ntohs(my_sockaddr.in6.sin6_port));
- }
- }
- }
- }
-
- if (IS_UDP(transport)) {
- int c;
-
- for (c = 0; c < settings.num_threads_per_udp; c++) {
- /* this is guaranteed to hit all threads because we round-robin */
- dispatch_conn_new(sfd, conn_read, EV_READ | EV_PERSIST,
- UDP_READ_BUFFER_SIZE, transport);
- }
- } else {
- if (!(listen_conn_add = conn_new(sfd, conn_listening,
- EV_READ | EV_PERSIST, 1,
- transport, main_base))) {
- fprintf(stderr, "failed to create listening connection\n");
- exit(EXIT_FAILURE);
- }
- listen_conn_add->next = listen_conn;
- listen_conn = listen_conn_add;
- }
- }
-
- freeaddrinfo(ai);
-
- /* Return zero iff we detected no errors in starting up connections */
- return success == 0;
-}
-
-static int server_sockets(int port, enum network_transport transport,
- FILE *portnumber_file) {
- if (settings.inter == NULL) {
- return server_socket(settings.inter, port, transport, portnumber_file);
- } else {
- // tokenize them and bind to each one of them..
- char *b;
- int ret = 0;
- char *list = strdup(settings.inter);
-
- if (list == NULL) {
- fprintf(stderr, "Failed to allocate memory for parsing server interface string\n");
- return 1;
- }
- for (char *p = strtok_r(list, ";,", &b);
- p != NULL;
- p = strtok_r(NULL, ";,", &b)) {
- int the_port = port;
- char *s = strchr(p, ':');
- if (s != NULL) {
- *s = '\0';
- ++s;
- if (!safe_strtol(s, &the_port)) {
- fprintf(stderr, "Invalid port number: \"%s\"", s);
- return 1;
- }
- }
- if (strcmp(p, "*") == 0) {
- p = NULL;
- }
- ret |= server_socket(p, the_port, transport, portnumber_file);
- }
- free(list);
- return ret;
- }
-}
-
-static int new_socket_unix(void) {
- int sfd;
- int flags;
-
- if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
- perror("socket()");
- return -1;
- }
-
- if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
- fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
- perror("setting O_NONBLOCK");
- close(sfd);
- return -1;
- }
- return sfd;
-}
-
-static int server_socket_unix(const char *path, int access_mask) {
- int sfd;
- struct linger ling = {0, 0};
- struct sockaddr_un addr;
- struct stat tstat;
- int flags =1;
- int old_umask;
-
- if (!path) {
- return 1;
- }
-
- if ((sfd = new_socket_unix()) == -1) {
- return 1;
- }
-
- /*
- * Clean up a previous socket file if we left it around
- */
- if (lstat(path, &tstat) == 0) {
- if (S_ISSOCK(tstat.st_mode))
- unlink(path);
- }
-
- setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
- setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
- setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
-
- /*
- * the memset call clears nonstandard fields in some impementations
- * that otherwise mess things up.
- */
- memset(&addr, 0, sizeof(addr));
-
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
- assert(strcmp(addr.sun_path, path) == 0);
- old_umask = umask( ~(access_mask&0777));
- if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
- perror("bind()");
- close(sfd);
- umask(old_umask);
- return 1;
- }
- umask(old_umask);
- if (listen(sfd, settings.backlog) == -1) {
- perror("listen()");
- close(sfd);
- return 1;
- }
- if (!(listen_conn = conn_new(sfd, conn_listening,
- EV_READ | EV_PERSIST, 1,
- local_transport, main_base))) {
- fprintf(stderr, "failed to create listening connection\n");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
-
-/*
- * We keep the current time of day in a global variable that's updated by a
- * timer event. This saves us a bunch of time() system calls (we really only
- * need to get the time once a second, whereas there can be tens of thousands
- * of requests a second) and allows us to use server-start-relative timestamps
- * rather than absolute UNIX timestamps, a space savings on systems where
- * sizeof(time_t) > sizeof(unsigned int).
- */
-volatile rel_time_t current_time;
-static struct event clockevent;
-
-/* libevent uses a monotonic clock when available for event scheduling. Aside
- * from jitter, simply ticking our internal timer here is accurate enough.
- * Note that users who are setting explicit dates for expiration times *must*
- * ensure their clocks are correct before starting memcached. */
-static void clock_handler(const int fd, const short which, void *arg) {
- struct timeval t = {.tv_sec = 1, .tv_usec = 0};
- static bool initialized = false;
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- static bool monotonic = false;
- static time_t monotonic_start;
-#endif
-
- if (initialized) {
- /* only delete the event if it's actually there. */
- evtimer_del(&clockevent);
- } else {
- initialized = true;
- /* process_started is initialized to time() - 2. We initialize to 1 so
- * flush_all won't underflow during tests. */
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
- monotonic = true;
- monotonic_start = ts.tv_sec - 2;
- }
-#endif
- }
-
- evtimer_set(&clockevent, clock_handler, 0);
- event_base_set(main_base, &clockevent);
- evtimer_add(&clockevent, &t);
-
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- if (monotonic) {
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
- return;
- current_time = (rel_time_t) (ts.tv_sec - monotonic_start);
- return;
- }
-#endif
- {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- current_time = (rel_time_t) (tv.tv_sec - process_started);
- }
-}
-
-static void usage(void) {
- printf(PACKAGE " " VERSION "\n");
- printf("-p <num> TCP port number to listen on (default: 11211)\n"
- "-U <num> UDP port number to listen on (default: 11211, 0 is off)\n"
- "-s <file> UNIX socket path to listen on (disables network support)\n"
- "-a <mask> access mask for UNIX socket, in octal (default: 0700)\n"
- "-l <addr> interface to listen on (default: INADDR_ANY, all addresses)\n"
- " <addr> may be specified as host:port. If you don't specify\n"
- " a port number, the value you specified with -p or -U is\n"
- " used. You may specify multiple addresses separated by comma\n"
- " or by using -l multiple times\n"
-
- "-d run as a daemon\n"
- "-r maximize core file limit\n"
- "-u <username> assume identity of <username> (only when run as root)\n"
- "-m <num> max memory to use for items in megabytes (default: 64 MB)\n"
- "-M return error on memory exhausted (rather than removing items)\n"
- "-c <num> max simultaneous connections (default: 1024)\n"
- "-k lock down all paged memory. Note that there is a\n"
- " limit on how much memory you may lock. Trying to\n"
- " allocate more than that would fail, so be sure you\n"
- " set the limit correctly for the user you started\n"
- " the daemon with (not for -u <username> user;\n"
- " under sh this is done with 'ulimit -S -l NUM_KB').\n"
- "-v verbose (print errors/warnings while in event loop)\n"
- "-vv very verbose (also print client commands/reponses)\n"
- "-vvv extremely verbose (also print internal state transitions)\n"
- "-h print this help and exit\n"
- "-i print memcached and libevent license\n"
- "-P <file> save PID in <file>, only used with -d option\n"
- "-f <factor> chunk size growth factor (default: 1.25)\n"
- "-n <bytes> minimum space allocated for key+value+flags (default: 48)\n");
- printf("-L Try to use large memory pages (if available). Increasing\n"
- " the memory page size could reduce the number of TLB misses\n"
- " and improve the performance. In order to get large pages\n"
- " from the OS, memcached will allocate the total item-cache\n"
- " in one large chunk.\n");
- printf("-D <char> Use <char> as the delimiter between key prefixes and IDs.\n"
- " This is used for per-prefix stats reporting. The default is\n"
- " \":\" (colon). If this option is specified, stats collection\n"
- " is turned on automatically; if not, then it may be turned on\n"
- " by sending the \"stats detail on\" command to the server.\n");
- printf("-t <num> number of threads to use (default: 4)\n");
- printf("-R Maximum number of requests per event, limits the number of\n"
- " requests process for a given connection to prevent \n"
- " starvation (default: 20)\n");
- printf("-C Disable use of CAS\n");
- printf("-b Set the backlog queue limit (default: 1024)\n");
- printf("-B Binding protocol - one of ascii, binary, or auto (default)\n");
- printf("-I Override the size of each slab page. Adjusts max item size\n"
- " (default: 1mb, min: 1k, max: 128m)\n");
-#ifdef ENABLE_SASL
- printf("-S Turn on Sasl authentication\n");
-#endif
- printf("-o Comma separated list of extended or experimental options\n"
- " - (EXPERIMENTAL) maxconns_fast: immediately close new\n"
- " connections if over maxconns limit\n"
- " - hashpower: An integer multiplier for how large the hash\n"
- " table should be. Can be grown at runtime if not big enough.\n"
- " Set this based on \"STAT hash_power_level\" before a \n"
- " restart.\n"
- );
- return;
-}
-
-static void usage_license(void) {
- printf(PACKAGE " " VERSION "\n\n");
- printf(
- "Copyright (c) 2003, Danga Interactive, Inc. <http://www.danga.com/>\n"
- "All rights reserved.\n"
- "\n"
- "Redistribution and use in source and binary forms, with or without\n"
- "modification, are permitted provided that the following conditions are\n"
- "met:\n"
- "\n"
- " * Redistributions of source code must retain the above copyright\n"
- "notice, this list of conditions and the following disclaimer.\n"
- "\n"
- " * Redistributions in binary form must reproduce the above\n"
- "copyright notice, this list of conditions and the following disclaimer\n"
- "in the documentation and/or other materials provided with the\n"
- "distribution.\n"
- "\n"
- " * Neither the name of the Danga Interactive nor the names of its\n"
- "contributors may be used to endorse or promote products derived from\n"
- "this software without specific prior written permission.\n"
- "\n"
- "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
- "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
- "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
- "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
- "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
- "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
- "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
- "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
- "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
- "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
- "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
- "\n"
- "\n"
- "This product includes software developed by Niels Provos.\n"
- "\n"
- "[ libevent ]\n"
- "\n"
- "Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>\n"
- "All rights reserved.\n"
- "\n"
- "Redistribution and use in source and binary forms, with or without\n"
- "modification, are permitted provided that the following conditions\n"
- "are met:\n"
- "1. Redistributions of source code must retain the above copyright\n"
- " notice, this list of conditions and the following disclaimer.\n"
- "2. Redistributions in binary form must reproduce the above copyright\n"
- " notice, this list of conditions and the following disclaimer in the\n"
- " documentation and/or other materials provided with the distribution.\n"
- "3. All advertising materials mentioning features or use of this software\n"
- " must display the following acknowledgement:\n"
- " This product includes software developed by Niels Provos.\n"
- "4. The name of the author may not be used to endorse or promote products\n"
- " derived from this software without specific prior written permission.\n"
- "\n"
- "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
- "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
- "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
- "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
- "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"
- "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
- "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
- "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
- "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n"
- "THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
- );
-
- return;
-}
-
-static void save_pid(const char *pid_file) {
- FILE *fp;
- if (access(pid_file, F_OK) == 0) {
- if ((fp = fopen(pid_file, "r")) != NULL) {
- char buffer[1024];
- if (fgets(buffer, sizeof(buffer), fp) != NULL) {
- unsigned int pid;
- if (safe_strtoul(buffer, &pid) && kill((pid_t)pid, 0) == 0) {
- fprintf(stderr, "WARNING: The pid file contained the following (running) pid: %u\n", pid);
- }
- }
- fclose(fp);
- }
- }
-
- if ((fp = fopen(pid_file, "w")) == NULL) {
- vperror("Could not open the pid file %s for writing", pid_file);
- return;
- }
-
- fprintf(fp,"%ld\n", (long)getpid());
- if (fclose(fp) == -1) {
- vperror("Could not close the pid file %s", pid_file);
- }
-}
-
-static void remove_pidfile(const char *pid_file) {
- if (pid_file == NULL)
- return;
-
- if (unlink(pid_file) != 0) {
- vperror("Could not remove the pid file %s", pid_file);
- }
-
-}
-
-static void sig_handler(const int sig) {
- printf("SIGINT handled.\n");
- exit(EXIT_SUCCESS);
-}
-
-#ifndef HAVE_SIGIGNORE
-static int sigignore(int sig) {
- struct sigaction sa = { .sa_handler = SIG_IGN, .sa_flags = 0 };
-
- if (sigemptyset(&sa.sa_mask) == -1 || sigaction(sig, &sa, 0) == -1) {
- return -1;
- }
- return 0;
-}
-#endif
-
-
-/*
- * On systems that supports multiple page sizes we may reduce the
- * number of TLB-misses by using the biggest available page size
- */
-static int enable_large_pages(void) {
-#if defined(HAVE_GETPAGESIZES) && defined(HAVE_MEMCNTL)
- int ret = -1;
- size_t sizes[32];
- int avail = getpagesizes(sizes, 32);
- if (avail != -1) {
- size_t max = sizes[0];
- struct memcntl_mha arg = {0};
- int ii;
-
- for (ii = 1; ii < avail; ++ii) {
- if (max < sizes[ii]) {
- max = sizes[ii];
- }
- }
-
- arg.mha_flags = 0;
- arg.mha_pagesize = max;
- arg.mha_cmd = MHA_MAPSIZE_BSSBRK;
-
- if (memcntl(0, 0, MC_HAT_ADVISE, (caddr_t)&arg, 0, 0) == -1) {
- fprintf(stderr, "Failed to set large pages: %s\n",
- strerror(errno));
- fprintf(stderr, "Will use default page size\n");
- } else {
- ret = 0;
- }
- } else {
- fprintf(stderr, "Failed to get supported pagesizes: %s\n",
- strerror(errno));
- fprintf(stderr, "Will use default page size\n");
- }
-
- return ret;
-#else
- return -1;
-#endif
-}
-
-/**
- * Do basic sanity check of the runtime environment
- * @return true if no errors found, false if we can't use this env
- */
-static bool sanitycheck(void) {
- /* One of our biggest problems is old and bogus libevents */
- const char *ever = event_get_version();
- if (ever != NULL) {
- if (strncmp(ever, "1.", 2) == 0) {
- /* Require at least 1.3 (that's still a couple of years old) */
- if ((ever[2] == '1' || ever[2] == '2') && !isdigit(ever[3])) {
- fprintf(stderr, "You are using libevent %s.\nPlease upgrade to"
- " a more recent version (1.3 or newer)\n",
- event_get_version());
- return false;
- }
- }
- }
-
- return true;
-}
-
-int main (int argc, char **argv) {
- int c;
- bool lock_memory = false;
- bool do_daemonize = false;
- bool preallocate = false;
- int maxcore = 0;
- char *username = NULL;
- char *pid_file = NULL;
- struct passwd *pw;
- struct rlimit rlim;
- char unit = '\0';
- int size_max = 0;
- int retval = EXIT_SUCCESS;
- /* listening sockets */
- static int *l_socket = NULL;
-
- /* udp socket */
- static int *u_socket = NULL;
- bool protocol_specified = false;
- bool tcp_specified = false;
- bool udp_specified = false;
-
- char *subopts;
- char *subopts_value;
- enum {
- MAXCONNS_FAST = 0,
- HASHPOWER_INIT,
- SLAB_REASSIGN,
- SLAB_AUTOMOVE
- };
- char *const subopts_tokens[] = {
- [MAXCONNS_FAST] = "maxconns_fast",
- [HASHPOWER_INIT] = "hashpower",
- [SLAB_REASSIGN] = "slab_reassign",
- [SLAB_AUTOMOVE] = "slab_automove",
- NULL
- };
-
- if (!sanitycheck()) {
- return EX_OSERR;
- }
-
- /* handle SIGINT */
- signal(SIGINT, sig_handler);
-
- /* init settings */
- settings_init();
-
- /* set stderr non-buffering (for running under, say, daemontools) */
- setbuf(stderr, NULL);
-
- /* process arguments */
- while (-1 != (c = getopt(argc, argv,
- "a:" /* access mask for unix socket */
- "p:" /* TCP port number to listen on */
- "s:" /* unix socket path to listen on */
- "U:" /* UDP port number to listen on */
- "m:" /* max memory to use for items in megabytes */
- "M" /* return error on memory exhausted */
- "c:" /* max simultaneous connections */
- "k" /* lock down all paged memory */
- "hi" /* help, licence info */
- "r" /* maximize core file limit */
- "v" /* verbose */
- "d" /* daemon mode */
- "l:" /* interface to listen on */
- "u:" /* user identity to run as */
- "P:" /* save PID in file */
- "f:" /* factor? */
- "n:" /* minimum space allocated for key+value+flags */
- "t:" /* threads */
- "D:" /* prefix delimiter? */
- "L" /* Large memory pages */
- "R:" /* max requests per event */
- "C" /* Disable use of CAS */
- "b:" /* backlog queue limit */
- "B:" /* Binding protocol */
- "I:" /* Max item size */
- "S" /* Sasl ON */
- "o:" /* Extended generic options */
- ))) {
- switch (c) {
- case 'a':
- /* access for unix domain socket, as octal mask (like chmod)*/
- settings.access= strtol(optarg,NULL,8);
- break;
-
- case 'U':
- settings.udpport = atoi(optarg);
- udp_specified = true;
- break;
- case 'p':
- settings.port = atoi(optarg);
- tcp_specified = true;
- break;
- case 's':
- settings.socketpath = optarg;
- break;
- case 'm':
- settings.maxbytes = ((size_t)atoi(optarg)) * 1024 * 1024;
- break;
- case 'M':
- settings.evict_to_free = 0;
- break;
- case 'c':
- settings.maxconns = atoi(optarg);
- break;
- case 'h':
- usage();
- exit(EXIT_SUCCESS);
- case 'i':
- usage_license();
- exit(EXIT_SUCCESS);
- case 'k':
- lock_memory = true;
- break;
- case 'v':
- settings.verbose++;
- break;
- case 'l':
- if (settings.inter != NULL) {
- size_t len = strlen(settings.inter) + strlen(optarg) + 2;
- char *p = malloc(len);
- if (p == NULL) {
- fprintf(stderr, "Failed to allocate memory\n");
- return 1;
- }
- snprintf(p, len, "%s,%s", settings.inter, optarg);
- free(settings.inter);
- settings.inter = p;
- } else {
- settings.inter= strdup(optarg);
- }
- break;
- case 'd':
- do_daemonize = true;
- break;
- case 'r':
- maxcore = 1;
- break;
- case 'R':
- settings.reqs_per_event = atoi(optarg);
- if (settings.reqs_per_event == 0) {
- fprintf(stderr, "Number of requests per event must be greater than 0\n");
- return 1;
- }
- break;
- case 'u':
- username = optarg;
- break;
- case 'P':
- pid_file = optarg;
- break;
- case 'f':
- settings.factor = atof(optarg);
- if (settings.factor <= 1.0) {
- fprintf(stderr, "Factor must be greater than 1\n");
- return 1;
- }
- break;
- case 'n':
- settings.chunk_size = atoi(optarg);
- if (settings.chunk_size == 0) {
- fprintf(stderr, "Chunk size must be greater than 0\n");
- return 1;
- }
- break;
- case 't':
- settings.num_threads = atoi(optarg);
- if (settings.num_threads <= 0) {
- fprintf(stderr, "Number of threads must be greater than 0\n");
- return 1;
- }
- /* There're other problems when you get above 64 threads.
- * In the future we should portably detect # of cores for the
- * default.
- */
- if (settings.num_threads > 64) {
- fprintf(stderr, "WARNING: Setting a high number of worker"
- "threads is not recommended.\n"
- " Set this value to the number of cores in"
- " your machine or less.\n");
- }
- break;
- case 'D':
- if (! optarg || ! optarg[0]) {
- fprintf(stderr, "No delimiter specified\n");
- return 1;
- }
- settings.prefix_delimiter = optarg[0];
- settings.detail_enabled = 1;
- break;
- case 'L' :
- if (enable_large_pages() == 0) {
- preallocate = true;
- } else {
- fprintf(stderr, "Cannot enable large pages on this system\n"
- "(There is no Linux support as of this version)\n");
- return 1;
- }
- break;
- case 'C' :
- settings.use_cas = false;
- break;
- case 'b' :
- settings.backlog = atoi(optarg);
- break;
- case 'B':
- protocol_specified = true;
- if (strcmp(optarg, "auto") == 0) {
- settings.binding_protocol = negotiating_prot;
- } else if (strcmp(optarg, "binary") == 0) {
- settings.binding_protocol = binary_prot;
- } else if (strcmp(optarg, "ascii") == 0) {
- settings.binding_protocol = ascii_prot;
- } else {
- fprintf(stderr, "Invalid value for binding protocol: %s\n"
- " -- should be one of auto, binary, or ascii\n", optarg);
- exit(EX_USAGE);
- }
- break;
- case 'I':
- unit = optarg[strlen(optarg)-1];
- if (unit == 'k' || unit == 'm' ||
- unit == 'K' || unit == 'M') {
- optarg[strlen(optarg)-1] = '\0';
- size_max = atoi(optarg);
- if (unit == 'k' || unit == 'K')
- size_max *= 1024;
- if (unit == 'm' || unit == 'M')
- size_max *= 1024 * 1024;
- settings.item_size_max = size_max;
- } else {
- settings.item_size_max = atoi(optarg);
- }
- if (settings.item_size_max < 1024) {
- fprintf(stderr, "Item max size cannot be less than 1024 bytes.\n");
- return 1;
- }
- if (settings.item_size_max > 1024 * 1024 * 128) {
- fprintf(stderr, "Cannot set item size limit higher than 128 mb.\n");
- return 1;
- }
- if (settings.item_size_max > 1024 * 1024) {
- fprintf(stderr, "WARNING: Setting item max size above 1MB is not"
- " recommended!\n"
- " Raising this limit increases the minimum memory requirements\n"
- " and will decrease your memory efficiency.\n"
- );
- }
- break;
- case 'S': /* set Sasl authentication to true. Default is false */
-#ifndef ENABLE_SASL
- fprintf(stderr, "This server is not built with SASL support.\n");
- exit(EX_USAGE);
-#endif
- settings.sasl = true;
- break;
- case 'o': /* It's sub-opts time! */
- subopts = optarg;
-
- while (*subopts != '\0') {
-
- switch (getsubopt(&subopts, subopts_tokens, &subopts_value)) {
- case MAXCONNS_FAST:
- settings.maxconns_fast = true;
- break;
- case HASHPOWER_INIT:
- if (subopts_value == NULL) {
- fprintf(stderr, "Missing numeric argument for hashpower\n");
- return 1;
- }
- settings.hashpower_init = atoi(subopts_value);
- if (settings.hashpower_init < 12) {
- fprintf(stderr, "Initial hashtable multiplier of %d is too low\n",
- settings.hashpower_init);
- return 1;
- } else if (settings.hashpower_init > 64) {
- fprintf(stderr, "Initial hashtable multiplier of %d is too high\n"
- "Choose a value based on \"STAT hash_power_level\" from a running instance\n",
- settings.hashpower_init);
- return 1;
- }
- break;
- case SLAB_REASSIGN:
- settings.slab_reassign = true;
- break;
- case SLAB_AUTOMOVE:
- if (subopts_value == NULL) {
- settings.slab_automove = 1;
- break;
- }
- settings.slab_automove = atoi(subopts_value);
- if (settings.slab_automove < 0 || settings.slab_automove > 2) {
- fprintf(stderr, "slab_automove must be between 0 and 2\n");
- return 1;
- }
- break;
- default:
- printf("Illegal suboption \"%s\"\n", subopts_value);
- return 1;
- }
-
- }
- break;
- default:
- fprintf(stderr, "Illegal argument \"%c\"\n", c);
- return 1;
- }
- }
-
- /*
- * Use one workerthread to serve each UDP port if the user specified
- * multiple ports
- */
- if (settings.inter != NULL && strchr(settings.inter, ',')) {
- settings.num_threads_per_udp = 1;
- } else {
- settings.num_threads_per_udp = settings.num_threads;
- }
-
- if (settings.sasl) {
- if (!protocol_specified) {
- settings.binding_protocol = binary_prot;
- } else {
- if (settings.binding_protocol != binary_prot) {
- fprintf(stderr, "ERROR: You cannot allow the ASCII protocol while using SASL.\n");
- exit(EX_USAGE);
- }
- }
- }
-
- if (tcp_specified && !udp_specified) {
- settings.udpport = settings.port;
- } else if (udp_specified && !tcp_specified) {
- settings.port = settings.udpport;
- }
-
- if (maxcore != 0) {
- struct rlimit rlim_new;
- /*
- * First try raising to infinity; if that fails, try bringing
- * the soft limit to the hard.
- */
- if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
- rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
- if (setrlimit(RLIMIT_CORE, &rlim_new)!= 0) {
- /* failed. try raising just to the old max */
- rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
- (void)setrlimit(RLIMIT_CORE, &rlim_new);
- }
- }
- /*
- * getrlimit again to see what we ended up with. Only fail if
- * the soft limit ends up 0, because then no core files will be
- * created at all.
- */
-
- if ((getrlimit(RLIMIT_CORE, &rlim) != 0) || rlim.rlim_cur == 0) {
- fprintf(stderr, "failed to ensure corefile creation\n");
- exit(EX_OSERR);
- }
- }
-
- /*
- * If needed, increase rlimits to allow as many connections
- * as needed.
- */
-
- if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
- fprintf(stderr, "failed to getrlimit number of files\n");
- exit(EX_OSERR);
- } else {
- rlim.rlim_cur = settings.maxconns;
- rlim.rlim_max = settings.maxconns;
- if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
- fprintf(stderr, "failed to set rlimit for open files. Try starting as root or requesting smaller maxconns value.\n");
- exit(EX_OSERR);
- }
- }
-
- /* lose root privileges if we have them */
- if (getuid() == 0 || geteuid() == 0) {
- if (username == 0 || *username == '\0') {
- fprintf(stderr, "can't run as root without the -u switch\n");
- exit(EX_USAGE);
- }
- if ((pw = getpwnam(username)) == 0) {
- fprintf(stderr, "can't find the user %s to switch to\n", username);
- exit(EX_NOUSER);
- }
- if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
- fprintf(stderr, "failed to assume identity of user %s\n", username);
- exit(EX_OSERR);
- }
- }
-
- /* Initialize Sasl if -S was specified */
- if (settings.sasl) {
- init_sasl();
- }
-
- /* daemonize if requested */
- /* if we want to ensure our ability to dump core, don't chdir to / */
- if (do_daemonize) {
- if (sigignore(SIGHUP) == -1) {
- perror("Failed to ignore SIGHUP");
- }
- if (daemonize(maxcore, settings.verbose) == -1) {
- fprintf(stderr, "failed to daemon() in order to daemonize\n");
- exit(EXIT_FAILURE);
- }
- }
-
- /* lock paged memory if needed */
- if (lock_memory) {
-#ifdef HAVE_MLOCKALL
- int res = mlockall(MCL_CURRENT | MCL_FUTURE);
- if (res != 0) {
- fprintf(stderr, "warning: -k invalid, mlockall() failed: %s\n",
- strerror(errno));
- }
-#else
- fprintf(stderr, "warning: -k invalid, mlockall() not supported on this platform. proceeding without.\n");
-#endif
- }
-
- /* initialize main thread libevent instance */
- main_base = event_init();
-
- /* initialize other stuff */
- stats_init();
- assoc_init(settings.hashpower_init);
- conn_init();
- slabs_init(settings.maxbytes, settings.factor, preallocate);
-
- /*
- * ignore SIGPIPE signals; we can use errno == EPIPE if we
- * need that information
- */
- if (sigignore(SIGPIPE) == -1) {
- perror("failed to ignore SIGPIPE; sigaction");
- exit(EX_OSERR);
- }
- /* start up worker threads if MT mode */
- thread_init(settings.num_threads, main_base);
-
- if (start_assoc_maintenance_thread() == -1) {
- exit(EXIT_FAILURE);
- }
-
- if (settings.slab_reassign &&
- start_slab_maintenance_thread() == -1) {
- exit(EXIT_FAILURE);
- }
-
- /* initialise clock event */
- clock_handler(0, 0, 0);
-
- /* create unix mode sockets after dropping privileges */
- if (settings.socketpath != NULL) {
- errno = 0;
- if (server_socket_unix(settings.socketpath,settings.access)) {
- vperror("failed to listen on UNIX socket: %s", settings.socketpath);
- exit(EX_OSERR);
- }
- }
-
- /* create the listening socket, bind it, and init */
- if (settings.socketpath == NULL) {
- const char *portnumber_filename = getenv("MEMCACHED_PORT_FILENAME");
- char temp_portnumber_filename[PATH_MAX];
- FILE *portnumber_file = NULL;
-
- if (portnumber_filename != NULL) {
- snprintf(temp_portnumber_filename,
- sizeof(temp_portnumber_filename),
- "%s.lck", portnumber_filename);
-
- portnumber_file = fopen(temp_portnumber_filename, "a");
- if (portnumber_file == NULL) {
- fprintf(stderr, "Failed to open \"%s\": %s\n",
- temp_portnumber_filename, strerror(errno));
- }
- }
-
- errno = 0;
- if (settings.port && server_sockets(settings.port, tcp_transport,
- portnumber_file)) {
- vperror("failed to listen on TCP port %d", settings.port);
- exit(EX_OSERR);
- }
-
- /*
- * initialization order: first create the listening sockets
- * (may need root on low ports), then drop root if needed,
- * then daemonise if needed, then init libevent (in some cases
- * descriptors created by libevent wouldn't survive forking).
- */
-
- /* create the UDP listening socket and bind it */
- errno = 0;
- if (settings.udpport && server_sockets(settings.udpport, udp_transport,
- portnumber_file)) {
- vperror("failed to listen on UDP port %d", settings.udpport);
- exit(EX_OSERR);
- }
-
- if (portnumber_file) {
- fclose(portnumber_file);
- rename(temp_portnumber_filename, portnumber_filename);
- }
- }
-
- /* Give the sockets a moment to open. I know this is dumb, but the error
- * is only an advisory.
- */
- usleep(1000);
- if (stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {
- fprintf(stderr, "Maxconns setting is too low, use -c to increase.\n");
- exit(EXIT_FAILURE);
- }
-
- if (pid_file != NULL) {
- save_pid(pid_file);
- }
-
- /* Drop privileges no longer needed */
- drop_privileges();
-
- /* enter the event loop */
- if (event_base_loop(main_base, 0) != 0) {
- retval = EXIT_FAILURE;
- }
-
- stop_assoc_maintenance_thread();
-
- /* remove the PID file if we're a daemon */
- if (do_daemonize)
- remove_pidfile(pid_file);
- /* Clean up strdup() call for bind() address */
- if (settings.inter)
- free(settings.inter);
- if (l_socket)
- free(l_socket);
- if (u_socket)
- free(u_socket);
-
- return retval;
-}
+++ /dev/null
-/* -*- 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 <stdbool.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <netinet/in.h>
-#include <event.h>
-#include <netdb.h>
-#include <pthread.h>
-#include <unistd.h>
-
-#include "protocol_binary.h"
-#include "cache.h"
-
-#include "sasl_defs.h"
-
-/** Maximum length of a key. */
-#define KEY_MAX_LENGTH 250
-
-/** Size of an incr buf. */
-#define INCR_MAX_STORAGE_LEN 24
-
-#define DATA_BUFFER_SIZE 2048
-#define UDP_READ_BUFFER_SIZE 65536
-#define UDP_MAX_PAYLOAD_SIZE 1400
-#define UDP_HEADER_SIZE 8
-#define MAX_SENDBUF_SIZE (256 * 1024 * 1024)
-/* I'm told the max length of a 64-bit num converted to string is 20 bytes.
- * Plus a few for spaces, \r\n, \0 */
-#define SUFFIX_SIZE 24
-
-/** Initial size of list of items being returned by "get". */
-#define ITEM_LIST_INITIAL 200
-
-/** Initial size of list of CAS suffixes appended to "gets" lines. */
-#define SUFFIX_LIST_INITIAL 20
-
-/** Initial size of the sendmsg() scatter/gather array. */
-#define IOV_LIST_INITIAL 400
-
-/** Initial number of sendmsg() argument structures to allocate. */
-#define MSG_LIST_INITIAL 10
-
-/** High water marks for buffer shrinking */
-#define READ_BUFFER_HIGHWAT 8192
-#define ITEM_LIST_HIGHWAT 400
-#define IOV_LIST_HIGHWAT 600
-#define MSG_LIST_HIGHWAT 100
-
-/* Binary protocol stuff */
-#define MIN_BIN_PKT_LENGTH 16
-#define BIN_PKT_HDR_WORDS (MIN_BIN_PKT_LENGTH/sizeof(uint32_t))
-
-/* Initial power multiplier for the hash table */
-#define HASHPOWER_DEFAULT 16
-
-/* unistd.h is here */
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-/* Slab sizing definitions. */
-#define POWER_SMALLEST 1
-#define POWER_LARGEST 200
-#define CHUNK_ALIGN_BYTES 8
-#define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1)
-
-/** How long an object can reasonably be assumed to be locked before
- harvesting it on a low memory condition. */
-#define TAIL_REPAIR_TIME (3 * 3600)
-
-/* 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 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_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
-};
-
-#define IS_UDP(x) (x == udp_transport)
-
-#define NREAD_ADD 1
-#define NREAD_SET 2
-#define NREAD_REPLACE 3
-#define NREAD_APPEND 4
-#define NREAD_PREPEND 5
-#define NREAD_CAS 6
-
-enum store_item_type {
- NOT_STORED=0, STORED, EXISTS, NOT_FOUND
-};
-
-enum delta_result_type {
- OK, NON_NUMERIC, EOM, DELTA_ITEM_NOT_FOUND, DELTA_ITEM_CAS_MISMATCH
-};
-
-/** Time relative to server start. Smaller than time_t on 64-bit systems. */
-typedef unsigned int rel_time_t;
-
-/** Stats stored per slab (and per thread). */
-struct slab_stats {
- uint64_t set_cmds;
- uint64_t get_hits;
- uint64_t touch_hits;
- uint64_t delete_hits;
- uint64_t cas_hits;
- uint64_t cas_badval;
- uint64_t incr_hits;
- uint64_t decr_hits;
-};
-
-/**
- * Stats stored per-thread.
- */
-struct thread_stats {
- pthread_mutex_t mutex;
- uint64_t get_cmds;
- uint64_t get_misses;
- uint64_t touch_cmds;
- uint64_t touch_misses;
- uint64_t delete_misses;
- uint64_t incr_misses;
- uint64_t decr_misses;
- uint64_t cas_misses;
- uint64_t bytes_read;
- uint64_t bytes_written;
- uint64_t flush_cmds;
- uint64_t conn_yields; /* # of yields for connections (-R option)*/
- uint64_t auth_cmds;
- uint64_t auth_errors;
- struct slab_stats slab_stats[MAX_NUMBER_OF_SLAB_CLASSES];
-};
-
-/**
- * Global stats.
- */
-struct stats {
- pthread_mutex_t mutex;
- unsigned int curr_items;
- unsigned int total_items;
- uint64_t curr_bytes;
- unsigned int curr_conns;
- unsigned int total_conns;
- uint64_t rejected_conns;
- unsigned int reserved_fds;
- unsigned int conn_structs;
- uint64_t get_cmds;
- uint64_t set_cmds;
- uint64_t touch_cmds;
- uint64_t get_hits;
- uint64_t get_misses;
- uint64_t touch_hits;
- uint64_t touch_misses;
- uint64_t evictions;
- uint64_t reclaimed;
- time_t started; /* when the process was started */
- bool accepting_conns; /* whether we are currently accepting */
- uint64_t listen_disabled_num;
- unsigned int hash_power_level; /* Better hope it's not over 9000 */
- uint64_t hash_bytes; /* size used for hash tables */
- bool hash_is_expanding; /* If the hash table is being expanded */
- uint64_t expired_unfetched; /* items reclaimed but never touched */
- uint64_t evicted_unfetched; /* items evicted but never touched */
- bool slab_reassign_running; /* slab reassign in progress */
- uint64_t slabs_moved; /* times slabs were moved around */
-};
-
-#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 */
- int evict_to_free;
- char *socketpath; /* path to unix socket if using local socket */
- int access; /* access mask (a la chmod) for unix domain socket */
- double factor; /* chunk size growth factor */
- int chunk_size;
- int num_threads; /* number of worker (without dispatcher) libevent threads to run */
- int num_threads_per_udp; /* number of worker threads serving each udp socket */
- char prefix_delimiter; /* character that marks a key prefix (for stats) */
- int detail_enabled; /* nonzero if we're collecting detailed stats */
- int reqs_per_event; /* Maximum number of io to process on each
- io-event. */
- bool use_cas;
- enum protocol binding_protocol;
- int backlog;
- int item_size_max; /* Maximum item size, and upper end for slabs */
- bool sasl; /* SASL on/off */
- bool maxconns_fast; /* Whether or not to early close connections */
- bool slab_reassign; /* Whether or not slab reassignment is allowed */
- int slab_automove; /* Whether or not to automatically move slabs */
- int hashpower_init; /* Starting hash power level */
-};
-
-extern struct stats stats;
-extern time_t process_started;
-extern struct settings settings;
-
-#define ITEM_LINKED 1
-#define ITEM_CAS 2
-
-/* temp */
-#define ITEM_SLABBED 4
-
-#define ITEM_FETCHED 8
-
-/**
- * Structure for storing items within memcached.
- */
-typedef struct _stritem {
- 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 */
- /* this odd type prevents type-punning issues when we do
- * the little shuffle to save space when not using CAS. */
- union {
- uint64_t cas;
- char end;
- } data[];
- /* if it_flags & ITEM_CAS we have 8 bytes CAS */
- /* then null-terminated key */
- /* then " flags length\r\n" (no terminating null) */
- /* then data with terminating \r\n (no terminating null; it's binary!) */
-} item;
-
-typedef struct {
- pthread_t thread_id; /* unique ID of this thread */
- struct event_base *base; /* libevent handle this thread uses */
- struct event notify_event; /* listen event for notify pipe */
- int notify_receive_fd; /* receiving end of notify pipe */
- int notify_send_fd; /* sending end of notify pipe */
- struct thread_stats stats; /* Stats generated by this thread */
- struct conn_queue *new_conn_queue; /* queue of new connections to handle */
- cache_t *suffix_cache; /* suffix cache */
-} LIBEVENT_THREAD;
-
-typedef struct {
- pthread_t thread_id; /* unique ID of this thread */
- struct event_base *base; /* libevent handle this thread uses */
-} LIBEVENT_DISPATCHER_THREAD;
-
-/**
- * The structure representing a connection into memcached.
- */
-typedef struct conn conn;
-struct conn {
- int sfd;
- sasl_conn_t *sasl_conn;
- enum conn_states state;
- enum bin_substates substate;
- struct event event;
- short ev_flags;
- short which; /** which events were just triggered */
-
- char *rbuf; /** buffer to read commands into */
- char *rcurr; /** but if we parsed some already, this is where we stopped */
- int rsize; /** total allocated size of rbuf */
- int rbytes; /** how much data, starting from rcur, do we have unparsed */
-
- char *wbuf;
- char *wcurr;
- int wsize;
- int wbytes;
- /** which state to go into after finishing current write */
- enum conn_states write_and_go;
- void *write_and_free; /** free this memory after finishing writing */
-
- char *ritem; /** when we read in an item's value, it goes here */
- int rlbytes;
-
- /* data for the nread state */
-
- /**
- * item is used to hold an item structure created after reading the command
- * line of set/add/replace commands, but before we finished reading the actual
- * data. The data is read into ITEM_data(item) to avoid extra copying.
- */
-
- void *item; /* for commands set/add/replace */
-
- /* data for the swallow state */
- int sbytes; /* how many bytes to swallow */
-
- /* data for the mwrite state */
- struct iovec *iov;
- int iovsize; /* number of elements allocated in iov[] */
- int iovused; /* number of elements used in iov[] */
-
- struct msghdr *msglist;
- int msgsize; /* number of elements allocated in msglist[] */
- int msgused; /* number of elements used in msglist[] */
- int msgcurr; /* element in msglist[] being transmitted now */
- int msgbytes; /* number of bytes in current msg */
-
- item **ilist; /* list of items to write out */
- int isize;
- item **icurr;
- int ileft;
-
- char **suffixlist;
- int suffixsize;
- char **suffixcurr;
- int suffixleft;
-
- enum protocol protocol; /* which protocol this connection speaks */
- enum network_transport transport; /* what transport is used by this connection */
-
- /* data for UDP clients */
- int request_id; /* Incoming UDP request ID, if this is a UDP "connection" */
- struct sockaddr request_addr; /* 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 */
-};
-
-
-/* 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;
- int busy_items;
- uint8_t done;
-};
-
-extern struct slab_rebalance slab_rebal;
-
-/*
- * Functions
- */
-void do_accept_new_conns(const bool do_accept);
-enum delta_result_type do_add_delta(conn *c, const char *key,
- const size_t nkey, const bool incr,
- const int64_t delta, char *buf,
- uint64_t *cas, const uint32_t hv);
-enum store_item_type do_store_item(item *item, int comm, conn* c, const uint32_t hv);
-conn *conn_new(const int sfd, const enum conn_states init_state, const int event_flags, const int read_buffer_size, enum network_transport transport, struct event_base *base);
-extern int daemonize(int nochdir, int noclose);
-
-static inline int mutex_lock(pthread_mutex_t *mutex)
-{
- while (pthread_mutex_trylock(mutex));
- return 0;
-}
-
-#define mutex_unlock(x) pthread_mutex_unlock(x)
-
-#include "stats.h"
-#include "slabs.h"
-#include "assoc.h"
-#include "items.h"
-#include "trace.h"
-#include "hash.h"
-#include "util.h"
-
-/*
- * Functions such as the libevent-related calls that need to do cross-thread
- * communication in multithreaded mode (rather than actually doing the work
- * in the current thread) are called via "dispatch_" frontends, which are
- * also #define-d to directly call the underlying code in singlethreaded mode.
- */
-
-void thread_init(int nthreads, struct event_base *main_base);
-int dispatch_event_add(int thread, conn *c);
-void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags, int read_buffer_size, enum network_transport transport);
-
-/* Lock wrappers for cache functions that are called from main loop. */
-enum delta_result_type add_delta(conn *c, const char *key,
- const size_t nkey, const int incr,
- const int64_t delta, char *buf,
- uint64_t *cas);
-void accept_new_conns(const bool do_accept);
-conn *conn_from_freelist(void);
-bool conn_add_to_freelist(conn *c);
-int is_listen_thread(void);
-item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes);
-char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
-void item_flush_expired(void);
-item *item_get(const char *key, const size_t nkey);
-item *item_touch(const char *key, const size_t nkey, uint32_t exptime);
-int item_link(item *it);
-void item_remove(item *it);
-int item_replace(item *it, item *new_it, const uint32_t hv);
-void item_stats(ADD_STAT add_stats, void *c);
-void item_stats_sizes(ADD_STAT add_stats, void *c);
-void item_unlink(item *it);
-void item_update(item *it);
-
-void item_lock(uint32_t hv);
-void item_unlock(uint32_t hv);
-unsigned short refcount_incr(unsigned short *refcount);
-unsigned short refcount_decr(unsigned short *refcount);
-void STATS_LOCK(void);
-void STATS_UNLOCK(void);
-void threadlocal_stats_reset(void);
-void threadlocal_stats_aggregate(struct thread_stats *stats);
-void slab_stats_aggregate(struct thread_stats *stats, struct slab_stats *out);
-
-/* Stat processing functions */
-void append_stat(const char *name, ADD_STAT add_stats, conn *c,
- const char *fmt, ...);
-
-enum store_item_type store_item(item *item, int comm, conn *c);
-
-#if HAVE_DROP_PRIVILEGES
-extern void drop_privileges(void);
-#else
-#define drop_privileges()
-#endif
-
-/* If supported, give compiler hints for branch prediction. */
-#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
-#define __builtin_expect(x, expected_value) (x)
-#endif
-
-#define likely(x) __builtin_expect((x),1)
-#define unlikely(x) __builtin_expect((x),0)
+++ /dev/null
-Name: memcached
-Version: @VERSION@
-Release: @RELEASE@%{?dist}
-Summary: High Performance, Distributed Memory Object Cache
-
-Group: System Environment/Daemons
-License: BSD
-URL: http://www.danga.com/memcached/
-Source0: http://memcached.googlecode.com/files/%{name}-@FULLVERSION@.tar.gz
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-
-BuildRequires: libevent-devel
-BuildRequires: perl(Test::More)
-BuildRequires: /usr/bin/prove
-Requires: initscripts
-Requires(post): /sbin/chkconfig
-Requires(preun): /sbin/chkconfig, /sbin/service
-Requires(postun): /sbin/service
-
-%description
-memcached is a high-performance, distributed memory object caching
-system, generic in nature, but intended for use in speeding up dynamic
-web applications by alleviating database load.
-
-%prep
-%setup -q -n %{name}-@FULLVERSION@
-
-
-%build
-%configure
-
-make %{?_smp_mflags}
-
-%check
-make test
-
-%install
-rm -rf %{buildroot}
-make install DESTDIR=%{buildroot}
-
-# remove memcached-debug
-rm -f %{buildroot}/%{_bindir}/memcached-debug
-
-# Perl script for monitoring memcached
-install -Dp -m0755 scripts/memcached-tool %{buildroot}%{_bindir}/memcached-tool
-
-# Init script
-install -Dp -m0755 scripts/memcached.sysv %{buildroot}%{_initrddir}/memcached
-
-# Default configs
-mkdir -p %{buildroot}/%{_sysconfdir}/sysconfig
-cat <<EOF >%{buildroot}/%{_sysconfdir}/sysconfig/%{name}
-PORT="11211"
-USER="nobody"
-MAXCONN="1024"
-CACHESIZE="64"
-OPTIONS=""
-EOF
-
-# pid directory
-mkdir -p %{buildroot}/%{_localstatedir}/run/memcached
-
-%clean
-rm -rf %{buildroot}
-
-
-%post
-/sbin/chkconfig --add %{name}
-
-%preun
-if [ "$1" = 0 ] ; then
- /sbin/service %{name} stop > /dev/null 2>&1
- /sbin/chkconfig --del %{name}
-fi
-exit 0
-
-%postun
-if [ "$1" -ge 1 ]; then
- /sbin/service %{name} condrestart > /dev/null 2>&1
-fi
-exit 0
-
-
-%files
-%defattr(-,root,root,-)
-%doc AUTHORS ChangeLog COPYING NEWS README.md doc/CONTRIBUTORS doc/*.txt
-%config(noreplace) %{_sysconfdir}/sysconfig/%{name}
-
-%dir %attr(750,nobody,nobody) %{_localstatedir}/run/memcached
-%{_bindir}/memcached-tool
-%{_bindir}/memcached
-%{_mandir}/man1/memcached.1*
-%{_initrddir}/memcached
-%{_includedir}/memcached
-
-%changelog
-* Mon Nov 2 2009 Dormando <dormando@rydia.net> - 1.4.3-1
-- Fix autogen more.
-
-* Sat Aug 29 2009 Dustin Sallings <dustin@spy.net> - 1.4.1-1
-- Autogenerate the version number from tags.
-
-* Wed Jul 4 2007 Paul Lindner <lindner@inuus.com> - 1.2.2-5
-- Use /var/run/memcached/ directory to hold PID file
-
-* Sat May 12 2007 Paul Lindner <lindner@inuus.com> - 1.2.2-4
-- Remove tabs from spec file, rpmlint reports no more errors
-
-* Thu May 10 2007 Paul Lindner <lindner@inuus.com> - 1.2.2-3
-- Enable build-time regression tests
-- add dependency on initscripts
-- remove memcached-debug (not needed in dist)
-- above suggestions from Bernard Johnson
-
-* Mon May 7 2007 Paul Lindner <lindner@inuus.com> - 1.2.2-2
-- Tidyness improvements suggested by Ruben Kerkhof in bugzilla #238994
-
-* Fri May 4 2007 Paul Lindner <lindner@inuus.com> - 1.2.2-1
-- Initial spec file created via rpmdev-newspec
+++ /dev/null
-/*
- * 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.
- */
-provider memcached {
- /**
- * Fired when a connection object is allocated from the connection pool.
- * @param connid the connection id
- */
- probe conn__allocate(int connid);
-
- /**
- * Fired when a connection object is released back to the connection pool.
- * @param connid the connection id
- */
- probe conn__release(int connid);
-
- /**
- * Fired when a new connection object is created (there are no more
- * connection objects in the connection pool).
- * @param ptr pointer to the connection object
- */
- probe conn__create(void *ptr);
-
- /**
- * Fired when a connection object is destroyed ("released back to
- * the memory subsystem").
- * @param ptr pointer to the connection object
- */
- probe conn__destroy(void *ptr);
-
- /**
- * Fired when a connection is dispatched from the "main thread" to a
- * worker thread.
- * @param connid the connection id
- * @param threadid the thread id
- */
- probe conn__dispatch(int connid, int threadid);
-
- /**
- * Allocate memory from the slab allocator.
- * @param size the requested size
- * @param slabclass the allocation will be fulfilled in this class
- * @param slabsize the size of each item in this class
- * @param ptr pointer to allocated memory
- */
- probe slabs__allocate(int size, int slabclass, int slabsize, void* ptr);
-
- /**
- * Failed to allocate memory (out of memory).
- * @param size the requested size
- * @param slabclass the class that failed to fulfill the request
- */
- probe slabs__allocate__failed(int size, int slabclass);
-
- /**
- * Fired when a slab class attempts to allocate more space.
- * @param slabclass class that needs more memory
- */
- probe slabs__slabclass__allocate(int slabclass);
-
- /**
- * Failed to allocate memory (out of memory).
- * @param slabclass the class that failed grab more memory
- */
- probe slabs__slabclass__allocate__failed(int slabclass);
-
- /**
- * Release memory.
- * @param size the size of the memory
- * @param slabclass the class the memory belongs to
- * @param ptr pointer to the memory to release
- */
- probe slabs__free(int size, int slabclass, void* ptr);
-
- /**
- * Fired when the when we have searched the hash table for a named key.
- * These two elements provide an insight in how well the hash function
- * functions. Long traversals are a sign of a less optimal function,
- * wasting cpu capacity.
- *
- * @param key the key searched for
- * @param keylen length of the key
- * @param depth the depth in the list of hash table
- */
- probe assoc__find(const char *key, int keylen, int depth);
-
- /**
- * Fired when a new item has been inserted.
- * @param key the key just inserted
- * @param keylen length of the key
- * @param nokeys the total number of keys currently stored,
- * including the key for which insert was called.
- */
- probe assoc__insert(const char *key, int keylen, int nokeys);
-
- /**
- * Fired when a new item has been removed.
- * @param key the key just deleted
- * @param keylen length of the key
- * @param nokeys the total number of keys currently stored,
- * excluding the key for which delete was called.
- */
- probe assoc__delete(const char *key, int keylen, int nokeys);
-
- /**
- * Fired when an item is linked into the cache.
- * @param key the items key
- * @param keylen length of the key
- * @param size the size of the data
- */
- probe item__link(const char *key, int keylen, int size);
-
- /**
- * Fired when an item is deleted.
- * @param key the items key
- * @param keylen length of the key
- * @param size the size of the data
- */
- probe item__unlink(const char *key, int keylen, int size);
-
- /**
- * Fired when the refcount for an item is reduced.
- * @param key the items key
- * @param keylen length of the key
- * @param size the size of the data
- */
- probe item__remove(const char *key, int keylen, int size);
-
- /**
- * Fired when the "last refenced" time is updated.
- * @param key the items key
- * @param keylen length of the key
- * @param size the size of the data
- */
- probe item__update(const char *key, int keylen, int size);
-
- /**
- * Fired when an item is replaced with another item.
- * @param oldkey the key of the item to replace
- * @param oldkeylen the length of the old key
- * @param oldsize the size of the old item
- * @param newkey the key of the new item
- * @param newkeylen the length of the new key
- * @param newsize the size of the new item
- */
- probe item__replace(const char *oldkey, int oldkeylen, int oldsize,
- const char *newkey, int newkeylen, int newsize);
-
- /**
- * Fired when the processing of a command starts.
- * @param connid the connection id
- * @param request the incomming request
- * @param size the size of the request
- */
- probe process__command__start(int connid, const void *request, int size);
-
- /**
- * Fired when the processing of a command is done.
- * @param connid the connection id
- * @param respnse the response to send back to the client
- * @param size the size of the response
- */
- probe process__command__end(int connid, const void *response, int size);
-
- /**
- * Fired for a get-command
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size size of the key's data (or signed int -1 if not found)
- * @param casid the casid for the item
- */
- probe command__get(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for an add-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size the new size of the key's data (or signed int -1 if
- * not found)
- * @param casid the casid for the item
- */
- probe command__add(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for a set-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size the new size of the key's data (or signed int -1 if
- * not found)
- * @param casid the casid for the item
- */
- probe command__set(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for a replace-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size the new size of the key's data (or signed int -1 if
- * not found)
- * @param casid the casid for the item
- */
- probe command__replace(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for a prepend-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size the new size of the key's data (or signed int -1 if
- * not found)
- * @param casid the casid for the item
- */
- probe command__prepend(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for an append-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size the new size of the key's data (or signed int -1 if
- * not found)
- * @param casid the casid for the item
- */
- probe command__append(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for an touch-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size the new size of the key's data (or signed int -1 if
- * not found)
- * @param casid the casid for the item
- */
- probe command__touch(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for a cas-command.
- * @param connid connection id
- * @param key requested key
- * @param keylen length of the key
- * @param size size of the key's data (or signed int -1 if not found)
- * @param casid the cas id requested
- */
- probe command__cas(int connid, const char *key, int keylen, int size, int64_t casid);
-
- /**
- * Fired for an incr command.
- * @param connid connection id
- * @param key the requested key
- * @param keylen length of the key
- * @param val the new value
- */
- probe command__incr(int connid, const char *key, int keylen, int64_t val);
-
- /**
- * Fired for a decr command.
- * @param connid connection id
- * @param key the requested key
- * @param keylen length of the key
- * @param val the new value
- */
- probe command__decr(int connid, const char *key, int keylen, int64_t val);
-
- /**
- * Fired for a delete command.
- * @param connid connection id
- * @param key the requested key
- * @param keylen length of the key
- */
- probe command__delete(int connid, const char *key, int keylen);
-
-};
-
-#pragma D attributes Unstable/Unstable/Common provider memcached provider
-#pragma D attributes Private/Private/Common provider memcached module
-#pragma D attributes Private/Private/Common provider memcached function
-#pragma D attributes Unstable/Unstable/Common provider memcached name
-#pragma D attributes Unstable/Unstable/Common provider memcached args
+++ /dev/null
-/*
- * 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 <trond.norbye@sun.com>
- */
-
-#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;
-
- /**
- * Defintion 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 */
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#include "memcached.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sasl/saslplug.h>
-
-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_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");
- }
- }
-}
+++ /dev/null
-#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 <sasl/sasl.h>
-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 */
+++ /dev/null
-dormando's awesome memcached top
-
-A flexible 'top' like utility for viewing memcached clusters.
-
-Under development. Latest code is available at:
-http://github.com/dormando/damemtop
-
-See --help for full information.
-
-Requires 'AnyEvent', and 'YAML' libraries from CPAN:
-http://search.cpan.org/
-
-'AnyEvent' depends on 'common::sense' (also at CPAN).
-
-If you have a large cluster and want higher performance, find
-and install 'EV' from CPAN. AnyEvent will automagically use it
-and use epoll, kqeueue, etc, for socket handling.
-
-Pester me for questions/bugs/ideas. As of writing the util is
-in early release and missing many future features.
+++ /dev/null
-#!/usr/bin/perl
-# dormando's awesome memcached top utility!
-#
-# Copyright 2009 Dormando (dormando@rydia.net). All rights reserved.
-#
-# Use and distribution licensed under the BSD license. See
-# the COPYING file for full text.
-
-use strict;
-use warnings FATAL => 'all';
-
-use AnyEvent;
-use AnyEvent::Socket;
-use AnyEvent::Handle;
-use Getopt::Long;
-use YAML qw/Dump Load LoadFile/;
-use Term::ReadKey qw/ReadMode ReadKey GetTerminalSize/;
-
-our $VERSION = '0.1';
-
-my $CLEAR = `clear`;
-my @TERM_SIZE = ();
-$|++;
-
-my %opts = ();
-GetOptions(\%opts, 'help|h', 'config=s');
-
-if ($opts{help}) {
- show_help(); exit;
-}
-
-$SIG{INT} = sub {
- ReadMode('normal');
- print "\n";
- exit;
-};
-
-# TODO: make this load from central location, and merge in homedir changes.
-# then merge Getopt::Long stuff on top of that
-# TODO: Set a bunch of defaults and merge in.
-my $CONF = load_config();
-my %CONS = ();
-my $LAST_RUN = time; # time after the last loop cycle.
-my $TIME_SINCE_LAST_RUN = time; # time since last loop cycle.
-my $loop_timer;
-my $main_cond;
-my $prev_stats_results;
-
-my %display_modes = (
- 't' => \&display_top_mode,
- '?' => \&display_help_mode,
- 'h' => \&display_help_mode,
-);
-
-my %column_compute = (
- 'hostname' => { stats => [], code => \&compute_hostname},
- 'hit_rate' => { stats => ['get_hits', 'get_misses'],
- code => \&compute_hit_rate },
- 'fill_rate' => { stats => ['bytes', 'limit_maxbytes'],
- code => \&compute_fill_rate },
-);
-
-my %column_format = (
- 'hit_rate' => \&format_percent,
- 'fill_rate' => \&format_percent,
-);
-
-# This can collapse into %column_compute
-my %column_format_totals = (
- 'hit_rate' => 0,
- 'fill_rate' => 0,
-);
-
-ReadMode('cbreak');
-my $LAST_KEY = '';
-my $read_keys = AnyEvent->io (
- fh => \*STDIN, poll => 'r',
- cb => sub {
- $LAST_KEY = ReadKey(-1);
- # If there is a running timer, cancel it.
- # Don't want to interrupt a main loop run.
- # fire_main_loop()'s iteration will pick up the keypress.
- if ($loop_timer) {
- $loop_timer = undef;
- $main_cond->send;
- }
- }
-);
-
-# start main loop
-fire_main_loop();
-
-### AnyEvent related code.
-
-sub fire_main_loop {
- for (;;) {
- $loop_timer = undef;
- $main_cond = AnyEvent->condvar;
- my $time_taken = main_loop();
- my $delay = $CONF->{delay} - $time_taken;
- $delay = 0 if $delay < 0;
- $loop_timer = AnyEvent->timer(
- after => $delay,
- cb => $main_cond,
- );
- $main_cond->recv;
- }
-}
-
-sub main_loop {
- my $start = AnyEvent->now; # use ->time to find the end.
- maintain_connections();
-
- my $cv = AnyEvent->condvar;
-
- # FIXME: Need to dump early if there're no connected conns
- # FIXME: Make this only fetch stats from cons we care to visualize?
- # maybe keep everything anyway to maintain averages?
- my %stats_results = ();
- while (my ($hostname, $con) = each %CONS) {
- $cv->begin;
- call_stats($con, ['', 'items', 'slabs'], sub {
- $stats_results{$hostname} = shift;
- $cv->end;
- });
- }
- $cv->recv;
-
- # Short circuit since we don't have anything to compare to.
- unless ($prev_stats_results) {
- $prev_stats_results = \%stats_results;
- return $CONF->{delay};
- }
-
- # Semi-exact global time diff for stats that want to average
- # themselves per-second.
- my $this_run = AnyEvent->time;
- $TIME_SINCE_LAST_RUN = $this_run - $LAST_RUN;
- $LAST_RUN = $this_run;
-
- # Done all our fetches. Drive the display.
- display_run($prev_stats_results, \%stats_results);
- $prev_stats_results = \%stats_results;
-
- my $end = AnyEvent->time;
- my $diff = $LAST_RUN - $start;
- print "loop took: $diff";
- return $diff;
-}
-
-sub maintain_connections {
- my $cv = AnyEvent->condvar;
-
- $cv->begin (sub { shift->send });
- for my $host (@{$CONF->{servers}}) {
- next if $CONS{$host};
- $cv->begin;
- $CONS{$host} = connect_memcached($host, sub {
- if ($_[0] eq 'err') {
- print "Failed connecting to $host: ", $_[1], "\n";
- delete $CONS{$host};
- }
- $cv->end;
- });
- }
- $cv->end;
-
- $cv->recv;
-}
-
-sub connect_memcached {
- my ($fullhost, $cb) = @_;
- my ($host, $port) = split /:/, $fullhost;
-
- my $con; $con = AnyEvent::Handle->new (
- connect => [$host => $port],
- on_connect => sub {
- $cb->('con');
- },
- on_connect_error => sub {
- $cb->('err', $!);
- $con->destroy;
- },
- on_eof => sub {
- $cb->('err', $!);
- $con->destroy;
- },
- );
- return $con;
-}
-
-# Function's getting a little weird since I started optimizing it.
-# As of my first set of production tests, this routine is where we spend
-# almost all of our processing time.
-sub call_stats {
- my ($con, $cmds, $cb) = @_;
-
- my $stats = {};
- my $num_types = @$cmds;
-
- my $reader; $reader = sub {
- my ($con, $results) = @_;
- {
- my %temp = ();
- for my $line (split(/\n/, $results)) {
- my ($k, $v) = (split(/\s+/, $line))[1,2];
- $temp{$k} = $v;
- }
- $stats->{$cmds->[0]} = \%temp;
- }
- shift @$cmds;
- unless (@$cmds) {
- # Out of commands to process, return goodies.
- $cb->($stats);
- return;
- }
- };
-
- for my $cmd (@$cmds) {
- $con->push_write('stats ' . $cmd . "\n");
- $stats->{$cmd} = {};
- $con->push_read(line => "END\r\n", $reader);
- }
-}
-
-### Compute routines
-
-sub compute_hostname {
- return $_[0];
-}
-
-sub compute_hit_rate {
- my $s = $_[1];
- my $total = $s->{get_hits} + $s->{get_misses};
- return 'NA' unless $total;
- return $s->{get_hits} / $total;
-}
-
-sub compute_fill_rate {
- my $s = $_[1];
- return $s->{bytes} / $s->{limit_maxbytes};
-}
-
-sub format_column {
- my ($col, $val) = @_;
- my $res;
- $col =~ s/^all_//;
- if ($column_format{$col}) {
- if (ref($column_format{$col}) eq 'CODE') {
- return $column_format{$col}->($val);
- } else {
- return $val .= $column_format{$col};
- }
- } else {
- return format_commas($val);
- }
-}
-
-sub column_can_total {
- my $col = shift;
- $col =~ s/^all_//;
- return 1 unless exists $column_format_totals{$col};
- return $column_format_totals{$col};
-}
-
-### Display routines
-
-# If there isn't a specific column type computer, see if we just want to
-# look at the specific stat and return it.
-# If column is a generic type and of 'all_cmd_get' format, return the more
-# complete stat instead of the diffed stat.
-sub compute_column {
- my ($col, $host, $prev_stats, $curr_stats) = @_;
- my $diff_stats = 1;
- $diff_stats = 0 if ($col =~ s/^all_//);
-
- # Really should decide on whether or not to flatten the hash :/
- my $find_stat = sub {
- for my $type (keys %{$_[0]}) {
- return $_[0]->{$type}->{$_[1]} if exists $_[0]->{$type}->{$_[1]};
- }
- };
-
- my $diff_stat = sub {
- my $stat = shift;
- return 'NA' unless defined $find_stat->($curr_stats, $stat);
- if ($diff_stats) {
- my $diff = eval {
- return ($find_stat->($curr_stats, $stat)
- - $find_stat->($prev_stats, $stat))
- / $TIME_SINCE_LAST_RUN;
- };
- return 'NA' if ($@);
- return $diff;
- } else {
- return $find_stat->($curr_stats, $stat);
- }
- };
-
- if (my $comp = $column_compute{$col}) {
- my %s = ();
- for my $stat (@{$comp->{stats}}) {
- $s{$stat} = $diff_stat->($stat);
- }
- return $comp->{code}->($host, \%s);
- } else {
- return $diff_stat->($col);
- }
- return 'NA';
-}
-
-# We have a bunch of stats from a bunch of connections.
-# At this point we run a particular display mode, capture the lines, then
-# truncate and display them.
-sub display_run {
- my $prev_stats = shift;
- my $curr_stats = shift;
- @TERM_SIZE = GetTerminalSize;
- die "cannot detect terminal size" unless $TERM_SIZE[0] && $TERM_SIZE[1];
-
- if ($LAST_KEY eq 'q') {
- print "\n";
- ReadMode('normal'); exit;
- }
-
- if ($LAST_KEY ne $CONF->{mode} && exists $display_modes{$LAST_KEY}) {
- $CONF->{prev_mode} = $CONF->{mode};
- $CONF->{mode} = $LAST_KEY;
- } elsif ($CONF->{mode} eq 'h' || $CONF->{mode} eq '?') {
- # Bust out of help mode on any key.
- $CONF->{mode} = $CONF->{prev_mode};
- }
- my $lines = $display_modes{$CONF->{mode}}->($prev_stats, $curr_stats);
- display_lines($lines) if $lines;
-}
-
-# Default "top" mode.
-# create a set of computed columns as requested by the config.
-# this has gotten a little out of hand... needs more cleanup/abstraction.
-sub display_top_mode {
- my $prev_stats = shift;
- my $curr_stats = shift;
-
- my @columns = @{$CONF->{top_mode}->{columns}};
- my @rows = ();
- my @tot_row = ();
-
- # Round one.
- for my $host (sort keys %{$curr_stats}) {
- my @row = ();
- for my $colnum (0 .. @columns-1) {
- my $col = $columns[$colnum];
- my $res = compute_column($col, $host, $prev_stats->{$host},
- $curr_stats->{$host});
- $tot_row[$colnum] += $res if is_numeric($res);
- push @row, $res;
- }
- push(@rows, \@row);
- }
-
- # Sort rows by sort column (ascending or descending)
- if (my $sort = $CONF->{top_mode}->{sort_column}) {
- my $order = $CONF->{top_mode}->{sort_order} || 'asc';
- my $colnum = 0;
- for (0 .. @columns-1) { $colnum = $_ if $columns[$_] eq $sort; }
- my @newrows;
- if ($order eq 'asc') {
- if (is_numeric($rows[0]->[$colnum])) {
- @newrows = sort { $a->[$colnum] <=> $b->[$colnum] } @rows;
- } else {
- @newrows = sort { $a->[$colnum] cmp $b->[$colnum] } @rows;
- }
- } else {
- if (is_numeric($rows[0]->[$colnum])) {
- @newrows = sort { $b->[$colnum] <=> $a->[$colnum] } @rows;
- } else {
- @newrows = sort { $b->[$colnum] cmp $a->[$colnum] } @rows;
- }
- }
- @rows = @newrows;
- }
-
- # Format each column after the sort...
- {
- my @newrows = ();
- for my $row (@rows) {
- my @newrow = ();
- for my $colnum (0 .. @columns-1) {
- push @newrow, is_numeric($row->[$colnum]) ?
- format_column($columns[$colnum], $row->[$colnum]) :
- $row->[$colnum];
- }
- push @newrows, \@newrow;
- }
- @rows = @newrows;
- }
-
- # Create average and total rows.
- my @avg_row = ();
- for my $col (0 .. @columns-1) {
- if (is_numeric($tot_row[$col])) {
- my $countable_rows = 0;
- for my $row (@rows) {
- next unless $row->[$col];
- $countable_rows++ unless $row->[$col] eq 'NA';
- }
- $countable_rows = 1 unless $countable_rows;
- push @avg_row, format_column($columns[$col],
- sprintf('%.2f', $tot_row[$col] / $countable_rows));
- } else {
- push @avg_row, 'NA';
- }
- $tot_row[$col] = 'NA' unless defined $tot_row[$col];
- $tot_row[$col] = 'NA' unless (column_can_total($columns[$col]));
- $tot_row[$col] = format_column($columns[$col], $tot_row[$col])
- unless $tot_row[$col] eq 'NA';
- }
- unshift @rows, \@avg_row;
- unshift @rows, ['AVERAGE:'];
- unshift @rows, \@tot_row;
- unshift @rows, ['TOTAL:'];
-
- # Round two. Pass @rows into a function which returns an array with the
- # desired format spacing for each column.
- unshift @rows, \@columns;
- my $spacing = find_optimal_spacing(\@rows);
-
- my @display_lines = ();
- for my $row (@rows) {
- my $line = '';
- for my $col (0 .. @$row-1) {
- my $space = $spacing->[$col];
- $line .= sprintf("%-${space}s ", $row->[$col]);
- }
- push @display_lines, $line;
- }
-
- return \@display_lines;
-}
-
-sub display_help_mode {
- my $help = <<"ENDHELP";
-
-dormando's awesome memcached top utility version v$VERSION
-
-This early version requires you to edit the ~/.damemtop/damemtop.yaml
-(or /etc/damemtop.yaml) file in order to change options.
-See --help for more info.
-
-Hit any key to exit help.
-ENDHELP
- my @lines = split /\n/, $help;
- display_lines(\@lines);
- $LAST_KEY = ReadKey(0);
- return;
-}
-
-# Takes a set of lines, clears screen, dumps header, trims lines, etc
-# MAYBE: mode to wrap lines instead of trim them?
-sub display_lines {
- my $lines = shift;
-
- my $width = $TERM_SIZE[0];
- my $height_remain = $TERM_SIZE[1];
-
- unshift @$lines, display_header($width);
- clear_screen() unless $CONF->{no_clear};
-
- while (--$height_remain && @$lines) {
- # truncate too long lines.
- my $line = shift @$lines;
- $line = substr $line, 0, $width-1;
- print $line, "\n";
- }
-}
-
-sub display_header {
- my $topbar = 'damemtop: ' . scalar localtime;
- if ($CONF->{mode} eq 't' && $CONF->{top_mode}->{sort_column}) {
- $topbar .= ' [sort: ' . $CONF->{top_mode}->{sort_column} . ']';
- }
- $topbar .= ' [delay: ' . $CONF->{delay} . 's]';
- return $topbar;
-}
-
-### Utilities
-
-# find the optimal format spacing for each column, which is:
-# longest length of item in col + 2 (whitespace).
-sub find_optimal_spacing {
- my $rows = shift;
- my @maxes = ();
-
- my $num_cols = @{$rows->[0]};
- for my $row (@$rows) {
- for my $col (0 .. $num_cols-1) {
- $maxes[$col] = 0 unless $maxes[$col];
- next unless $row->[$col];
- $maxes[$col] = length($row->[$col])
- if length($row->[$col]) > $maxes[$col];
- }
- }
- for my $col (0 .. $num_cols) {
- $maxes[$col] += 1;
- }
-
- return \@maxes;
-}
-
-# doesn't try too hard to identify numbers...
-sub is_numeric {
- return 0 unless $_[0];
- return 1 if $_[0] =~ m/^\d+(\.\d*)?(\w+)?$/;
- return 0;
-}
-
-sub format_percent {
- return sprintf("%.2f%%", $_[0] * 100);
-}
-
-sub format_commas {
- my $num = shift;
- $num = int($num);
- $num =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g;
- return $num;
-}
-
-# Can tick counters/etc here as well.
-sub clear_screen {
- print $CLEAR;
-}
-
-# tries minimally to find a localized config file.
-# TODO: Handle the YAML error and make it prettier.
-sub load_config {
- my $config = $opts{config} if $opts{config};
- my $homedir = "$ENV{HOME}/.damemtop/damemtop.yaml";
- if (-e $homedir) {
- $config = $homedir;
- } else {
- $config = '/etc/damemtop.yaml';
- }
- return LoadFile($config);
-}
-
-sub show_help {
- print <<"ENDHELP";
-dormando's awesome memcached top utility version v$VERSION
-
-This program is copyright (c) 2009 Dormando.
-Use and distribution licensed under the BSD license. See
-the COPYING file for full text.
-
-contact: dormando\@rydia.net or memcached\@googlegroups.com.
-
-This early version requires you to edit the ~/.damemtop/damemtop.yaml
-(or /etc/damemtop.yaml) file in order to change options.
-
-You may display any column that is in the output of
-'stats', 'stats items', or 'stats slabs' from memcached's ASCII protocol.
-Start a column with 'all_' (ie; 'all_get_hits') to display the current stat,
-otherwise the stat is displayed as an average per second.
-
-Specify a "sort_column" under "top_mode" to sort the output by any column.
-
-Some special "computed" columns exist:
-hit_rate (get/miss hit ratio)
-fill_rate (% bytes used out of the maximum memory limit)
-ENDHELP
- exit;
-}
+++ /dev/null
-delay: 3
-mode: t
-top_mode:
- sort_column: "hostname"
- sort_order: "asc"
- columns:
- - hostname
- - all_version
- - all_fill_rate
- - hit_rate
- - evictions
- - bytes_written
- - "2:get_hits"
-servers:
- - 127.0.0.1:11211
- - 127.0.0.2:11211
+++ /dev/null
-#! /usr/bin/perl
-# See memcached for LICENSE
-# Copyright 2011 Dormando (dormando@rydia.net)
-
-=head1 NAME
-
-mc_slab_mover -- example utility for slab page reassignment for memcached
-
-=head1 SYNOPSIS
-
- $ mc_slab_mover --host="127.0.0.1:11211" --verbose
- $ mc_slab_mover --host="127.0.0.1:11211" --automove
- $ mc_slab_mover --host="127.0.0.1:11211" --sleep=60 --loops=4 --automove
-
-=head1 DESCRIPTION
-
-This utility is an example implementation of an algorithm for reassigning
-slab memory in a running memcached instance. If memcached's built-in
-automover isn't working for you, you may use this script as an example
-base and expand on it. We welcome modifications or alternatives on the
-mailing list.
-
-=head1 ALGORITHM
-
-The default algorithm is simple, and may serve for a common case: over
-time one slab may grow in use compare to others, and as evictions stop
-in one slab and start in another it will reassign memory.
-
-If a slab has the most evictions three times in a row, it will pull a page
-from a slab which has had zero evictions three times in a row.
-
-There are many traffic patterns where this does not work well. IE: If you
-never use expirations and rely on the LRU (so all slabs always evict),
-it will not be as likely to find source pages to move.
-
-=head1 OPTIONS
-
-=over
-
-=item --host="IP:PORT"
-
-The hostname to connect to. NOTE: If connection to the host breaks, script
-will stop.
-
-=item --sleep=10
-
-How long to wait between loops for gathering stats.
-
-=item --loops=3
-
-How many loops to run before making a decision for a move.
-
-=item --verbose
-
-Prints a formatted dump of some common statistics per loop.
-
-=item --automove
-
-Enables the automover, and will attempt to move memory around if it finds
-viable candidates.
-
-=back
-
-=head1 AUTHOR
-
-Dormando E<lt>L<dormando@rydia.net>E<gt>
-
-=head1 LICENSE
-
-Licensed for use and redistribution under the same terms as Memcached itself.
-
-=cut
-
-use warnings;
-use strict;
-
-use IO::Socket::INET;
-
-use FindBin;
-use Data::Dumper qw/Dumper/;
-use Getopt::Long;
-
-my %opts = ('sleep' => 10, automove => 0, verbose => 0, loops => 3);
-GetOptions(
- "host=s" => \$opts{host},
- "sleep=i" => \$opts{'sleep'},
- "loops=i" => \$opts{loops},
- "automove" => \$opts{automove},
- "verbose" => \$opts{verbose},
- ) or usage();
-
-die "Must specify at least --host='127.0.0.1:11211'" unless $opts{host};
-my $sock = IO::Socket::INET->new(PeerAddr => $opts{host},
- Timeout => 3);
-die "$!\n" unless $sock;
-
-my %stats = ();
-my %move = (winner => 0, wins => 0);
-
-$SIG{INT} = sub {
- print "STATS: ", Dumper(\%stats), "\n";
- exit;
-};
-$SIG{USR1} = sub {
- print "STATS: ", Dumper(\%stats), "\n";
-};
-run();
-
-sub usage {
- print qq{Usage:
- mc_slab_ratios --host="127.0.0.1:11211" --verbose --automove
- run `perldoc mc_slab_ratios` for full information
-
-};
- exit 1;
-}
-
-sub run {
- my $slabs_before = grab_stats();
-
- while (1) {
- sleep $opts{'sleep'};
- my $slabs_after = grab_stats();
-
- my ($totals, $sorted) = calc_results_evicted($slabs_before, $slabs_after);
-# my ($totals, $sorted) = calc_results_numratio($slabs_before, $slabs_after);
-
- my $pct = sub {
- my ($num, $divisor) = @_;
- return 0 unless $divisor;
- return ($num / $divisor);
- };
- if ($opts{verbose}) {
- printf " %02s: %-8s (pct ) %-10s (pct ) %-6s (pct ) get_hits (pct ) cmd_set (pct )\n",
- 'sb', 'evicted', 'items', 'pages';
- for my $slab (@$sorted) {
- printf " %02d: %-8d (%.2f%%) %-10s (%.4f%%) %-6d (%.2f%%) %-8d (%.3f%%) %-7d (%.2f%%)\n",
- $slab->{slab}, $slab->{evicted_d},
- $pct->($slab->{evicted_d}, $totals->{evicted_d}),
- $slab->{number},
- $pct->($slab->{number}, $totals->{number}),
- $slab->{total_pages},
- $pct->($slab->{total_pages}, $totals->{total_pages}),
- $slab->{get_hits_d},
- $pct->($slab->{get_hits_d}, $totals->{get_hits_d}),
- $slab->{cmd_set_d},
- $pct->($slab->{cmd_set_d}, $totals->{cmd_set_d});
- }
- }
-
- next unless @$sorted;
- my $highest = $sorted->[-1];
- $stats{$highest->{slab}}++;
- print " (winner: ", $highest->{slab}, " wins: ", $stats{$highest->{slab}}, ")\n";
- automove_basic($totals, $sorted) if ($opts{automove});
-
- $slabs_before = $slabs_after;
- }
-}
-
-sub grab_stats {
- my %slabs = ();
- for my $stat (qw/items slabs/) {
- print $sock "stats $stat\r\n";
- while (my $line = <$sock>) {
- chomp $line;
- last if ($line =~ m/^END/);
- if ($line =~ m/^STAT (?:items:)?(\d+):(\S+) (\S+)/) {
- my ($slab, $var, $val) = ($1, $2, $3);
- $slabs{$slab}->{$var} = $val;
- }
- }
- }
-
- return \%slabs;
-}
-
-# Really stupid algo, same as the initial algo built into memcached.
-# If a slab "wins" most evictions 3 times in a row, pick from a slab which
-# has had 0 evictions 3 times in a row and move it over.
-sub automove_basic {
- my ($totals, $sorted) = @_;
-
- my $source = 0;
- my $dest = 0;
- my $high = $sorted->[-1];
- return unless $high->{evicted_d} > 0;
- if ($move{winner} == $high->{slab}) {
- $move{wins}++;
- $dest = $move{winner} if $move{wins} >= $opts{loops};
- } else {
- $move{wins} = 1;
- $move{winner} = $high->{slab};
- }
- for my $slab (@$sorted) {
- my $id = $slab->{slab};
- if ($slab->{evicted_d} == 0 && $slab->{total_pages} > 2) {
- $move{zeroes}->{$id}++;
- $source = $id if (!$source && $move{zeroes}->{$id} >= $opts{loops});
- } else {
- delete $move{zeroes}->{$slab->{slab}}
- if exists $move{zeroes}->{$slab->{slab}};
- }
- }
-
- if ($source && $dest) {
- print " slabs reassign $source $dest\n";
- print $sock "slabs reassign $source $dest\r\n";
- my $res = <$sock>;
- print " RES: ", $res;
- } elsif ($dest && !$source) {
- print "FAIL: want to move memory to $dest but no valid source slab available\n";
- }
-}
-
-# Using just the evicted stats.
-sub calc_results_evicted {
- my ($slabs, $totals) = calc_slabs(@_);
- my @sorted = sort { $a->{evicted_d} <=> $b->{evicted_d} } values %$slabs;
- return ($totals, \@sorted);
-}
-
-# Weighted ratios of evictions vs total stored items
-# Seems to fail as an experiment, but it tries to weight stats.
-# In this case evictions in underused classes tend to get vastly inflated
-sub calc_results_numratio {
- my ($slabs, $totals) = calc_slabs(@_, sub {
- my ($sb, $sa, $s) = @_;
- if ($s->{evicted_d}) {
- $s->{numratio} = $s->{evicted_d} / $s->{number};
- } else { $s->{numratio} = 0; }
- });
- my @sorted = sort { $a->{numratio} <=> $b->{numratio} } values %$slabs;
- return ($totals, \@sorted);
-}
-
-sub calc_slabs {
- my ($slabs_before, $slabs_after, $code) = @_;
- my %slabs = ();
- my %totals = ();
- for my $id (keys %$slabs_after) {
- my $sb = $slabs_before->{$id};
- my $sa = $slabs_after->{$id};
- next unless ($sb && $sa);
- my %slab = %$sa;
- for my $key (keys %slab) {
- # Add totals, diffs
- if ($slab{$key} =~ m/^\d+$/) {
- $totals{$key} += $slab{$key};
- $slab{$key . '_d'} = $sa->{$key} - $sb->{$key};
- $totals{$key . '_d'} += $sa->{$key} - $sb->{$key};
- }
- }
- # External code
- $code->($sb, $sa, \%slab) if $code;
- $slab{slab} = $id;
- $slabs{$id} = \%slab;
- }
- return (\%slabs, \%totals);
-}
+++ /dev/null
-#! /bin/bash
-### BEGIN INIT INFO
-# Provides: memcached
-# Required-Start: $syslog
-# Required-Stop: $syslog
-# Should-Start: $local_fs
-# Should-Stop: $local_fs
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: memcached - Memory caching daemon
-# Description: memcached - Memory caching daemon
-### END INIT INFO
-
-# Usage:
-# cp /etc/memcached.conf /etc/memcached_server1.conf
-# cp /etc/memcached.conf /etc/memcached_server2.conf
-# start all instances:
-# /etc/init.d/memcached start
-# start one instance:
-# /etc/init.d/memcached start server1
-# stop all instances:
-# /etc/init.d/memcached stop
-# stop one instance:
-# /etc/init.d/memcached stop server1
-# There is no "status" command.
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DAEMON=/usr/bin/memcached
-DAEMONNAME=memcached
-DAEMONBOOTSTRAP=/usr/share/memcached/scripts/start-memcached
-DESC=memcached
-
-test -x $DAEMON || exit 0
-test -x $DAEMONBOOTSTRAP || exit 0
-
-set -e
-
-FILES=(/etc/memcached_*.conf)
-# check for alternative config schema
-if [ -r "${FILES[0]}" ]; then
- CONFIGS=()
- for FILE in "${FILES[@]}";
- do
- # remove prefix
- NAME=${FILE#/etc/}
- # remove suffix
- NAME=${NAME%.conf}
-
- # check optional second param
- if [ $# -ne 2 ];
- then
- # add to config array
- CONFIGS+=($NAME)
- elif [ "memcached_$2" == "$NAME" ];
- then
- # use only one memcached
- CONFIGS=($NAME)
- break;
- fi;
- done;
-
- if [ ${#CONFIGS[@]} == 0 ];
- then
- echo "Config not exist for: $2" >&2
- exit 1
- fi;
-else
- CONFIGS=(memcached)
-fi;
-
-CONFIG_NUM=${#CONFIGS[@]}
-for ((i=0; i < $CONFIG_NUM; i++)); do
- NAME=${CONFIGS[${i}]}
- PIDFILE="/var/run/${NAME}.pid"
-
-case "$1" in
- start)
- echo -n "Starting $DESC: "
- start-stop-daemon --start --quiet --exec "$DAEMONBOOTSTRAP" -- /etc/${NAME}.conf $PIDFILE
- echo "$NAME."
- ;;
- stop)
- echo -n "Stopping $DESC: "
- start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON
- echo "$NAME."
- rm -f $PIDFILE
- ;;
-
- restart|force-reload)
- #
- # If the "reload" option is implemented, move the "force-reload"
- # option to the "reload" entry above. If not, "force-reload" is
- # just the same as "restart".
- #
- echo -n "Restarting $DESC: "
- start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
- rm -f $PIDFILE
- sleep 1
- start-stop-daemon --start --quiet --exec "$DAEMONBOOTSTRAP" -- /etc/${NAME}.conf $PIDFILE
- echo "$NAME."
- ;;
- *)
- N=/etc/init.d/$NAME
- # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
- echo "Usage: $N {start|stop|restart|force-reload}" >&2
- exit 1
- ;;
-esac
-done;
-
-exit 0
+++ /dev/null
-#!/usr/bin/perl
-#
-# memcached-tool:
-# stats/management tool for memcached.
-#
-# Author:
-# Brad Fitzpatrick <brad@danga.com>
-#
-# License:
-# public domain. I give up all rights to this
-# tool. modify and copy at will.
-#
-
-use strict;
-use IO::Socket::INET;
-
-my $addr = shift;
-my $mode = shift || "display";
-my ($from, $to);
-
-if ($mode eq "display") {
- undef $mode if @ARGV;
-} elsif ($mode eq "move") {
- $from = shift;
- $to = shift;
- undef $mode if $from < 6 || $from > 17;
- undef $mode if $to < 6 || $to > 17;
- print STDERR "ERROR: parameters out of range\n\n" unless $mode;
-} elsif ($mode eq 'dump') {
- ;
-} elsif ($mode eq 'stats') {
- ;
-} else {
- undef $mode;
-}
-
-undef $mode if @ARGV;
-
-die
- "Usage: memcached-tool <host[:port] | /path/to/socket> [mode]\n
- memcached-tool 10.0.0.5:11211 display # shows slabs
- memcached-tool 10.0.0.5:11211 # same. (default is display)
- memcached-tool 10.0.0.5:11211 stats # shows general stats
- memcached-tool 10.0.0.5:11211 dump # dumps keys and values
-" unless $addr && $mode;
-
-
-my $sock;
-if ($addr =~ m:/:) {
- $sock = IO::Socket::UNIX->new(
- Peer => $addr,
- );
-}
-else {
- $addr .= ':11211' unless $addr =~ /:\d+$/;
-
- $sock = IO::Socket::INET->new(
- PeerAddr => $addr,
- Proto => 'tcp',
- );
-}
-die "Couldn't connect to $addr\n" unless $sock;
-
-if ($mode eq 'dump') {
- my %items;
- my $totalitems;
-
- print $sock "stats items\r\n";
-
- while (<$sock>) {
- last if /^END/;
- if (/^STAT items:(\d*):number (\d*)/) {
- $items{$1} = $2;
- $totalitems += $2;
- }
- }
- print STDERR "Dumping memcache contents\n";
- print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n";
- print STDERR " Number of items : $totalitems\n";
-
- foreach my $bucket (sort(keys(%items))) {
- print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n";
- print $sock "stats cachedump $bucket $items{$bucket}\r\n";
- my %keyexp;
- while (<$sock>) {
- last if /^END/;
- # return format looks like this
- # ITEM foo [6 b; 1176415152 s]
- if (/^ITEM (\S+) \[.* (\d+) s\]/) {
- $keyexp{$1} = $2;
- }
- }
-
- foreach my $k (keys(%keyexp)) {
- print $sock "get $k\r\n";
- my $response = <$sock>;
- if ($response =~ /VALUE (\S+) (\d+) (\d+)/) {
- my $flags = $2;
- my $len = $3;
- my $val;
- read $sock, $val, $len;
- print "add $k $flags $keyexp{$k} $len\r\n$val\r\n";
- # get the END
- $_ = <$sock>;
- $_ = <$sock>;
- }
- }
- }
- exit;
-}
-
-if ($mode eq 'stats') {
- my %items;
-
- print $sock "stats\r\n";
-
- while (<$sock>) {
- last if /^END/;
- chomp;
- if (/^STAT\s+(\S*)\s+(.*)/) {
- $items{$1} = $2;
- }
- }
- printf ("#%-17s %5s %11s\n", $addr, "Field", "Value");
- foreach my $name (sort(keys(%items))) {
- printf ("%24s %12s\n", $name, $items{$name});
-
- }
- exit;
-}
-
-# display mode:
-
-my %items; # class -> { number, age, chunk_size, chunks_per_page,
-# total_pages, total_chunks, used_chunks,
-# free_chunks, free_chunks_end }
-
-print $sock "stats items\r\n";
-my $max = 0;
-while (<$sock>) {
- last if /^END/;
- if (/^STAT items:(\d+):(\w+) (\d+)/) {
- $items{$1}{$2} = $3;
- $max = $1;
- }
-}
-
-print $sock "stats slabs\r\n";
-while (<$sock>) {
- last if /^END/;
- if (/^STAT (\d+):(\w+) (\d+)/) {
- $items{$1}{$2} = $3;
- }
-}
-
-print " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n";
-foreach my $n (1..$max) {
- my $it = $items{$n};
- next if (0 == $it->{total_pages});
- my $size = $it->{chunk_size} < 1024 ?
- "$it->{chunk_size}B" :
- sprintf("%.1fK", $it->{chunk_size} / 1024.0);
- my $full = $it->{free_chunks_end} == 0 ? "yes" : " no";
- printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n",
- $n, $size, $it->{age}, $it->{total_pages},
- $it->{number}, $full, $it->{evicted},
- $it->{evicted_time}, $it->{outofmemory});
-}
-
+++ /dev/null
-[Unit]
-Description=memcached daemon
-After=network.target
-
-[Service]
-EnvironmentFile=/etc/sysconfig/memcached
-ExecStart=/usr/bin/memcached -p ${PORT} -u ${USER} -m ${CACHESIZE} -c ${MAXCONN} $OPTIONS
-
-[Install]
-WantedBy=multi-user.target
+++ /dev/null
-#! /bin/sh
-#
-# chkconfig: - 55 45
-# description: The memcached daemon is a network memory cache service.
-# processname: memcached
-# config: /etc/sysconfig/memcached
-
-# Source function library.
-. /etc/rc.d/init.d/functions
-
-PORT=11211
-USER=nobody
-MAXCONN=1024
-CACHESIZE=64
-OPTIONS=""
-
-if [ -f /etc/sysconfig/memcached ];then
- . /etc/sysconfig/memcached
-fi
-
-# Check that networking is up.
-if [ "$NETWORKING" = "no" ]
-then
- exit 0
-fi
-
-RETVAL=0
-prog="memcached"
-
-start () {
- echo -n $"Starting $prog: "
- # insure that /var/run/memcached has proper permissions
- chown $USER /var/run/memcached
- daemon memcached -d -p $PORT -u $USER -m $CACHESIZE -c $MAXCONN -P /var/run/memcached/memcached.pid $OPTIONS
- RETVAL=$?
- echo
- [ $RETVAL -eq 0 ] && touch /var/lock/subsys/memcached
-}
-stop () {
- echo -n $"Stopping $prog: "
- killproc memcached
- RETVAL=$?
- echo
- if [ $RETVAL -eq 0 ] ; then
- rm -f /var/lock/subsys/memcached
- rm -f /var/run/memcached.pid
- fi
-}
-
-restart () {
- stop
- start
-}
-
-
-# See how we were called.
-case "$1" in
- start)
- start
- ;;
- stop)
- stop
- ;;
- status)
- status memcached
- ;;
- restart|reload)
- restart
- ;;
- condrestart)
- [ -f /var/lock/subsys/memcached ] && restart || :
- ;;
- *)
- echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
- exit 1
-esac
-
-exit $?
+++ /dev/null
-#!/usr/bin/perl -w
-
-# start-memcached
-# 2003/2004 - Jay Bonci <jaybonci@debian.org>
-# This script handles the parsing of the /etc/memcached.conf file
-# and was originally created for the Debian distribution.
-# Anyone may use this little script under the same terms as
-# memcached itself.
-
-use POSIX qw(setsid);
-use strict;
-
-if($> != 0 and $< != 0)
-{
- print STDERR "Only root wants to run start-memcached.\n";
- exit;
-}
-
-my $params; my $etchandle; my $etcfile = "/etc/memcached.conf";
-
-# This script assumes that memcached is located at /usr/bin/memcached, and
-# that the pidfile is writable at /var/run/memcached.pid
-
-my $memcached = "/usr/bin/memcached";
-my $pidfile = "/var/run/memcached.pid";
-
-if (scalar(@ARGV) == 2) {
- $etcfile = shift(@ARGV);
- $pidfile = shift(@ARGV);
-}
-
-# If we don't get a valid logfile parameter in the /etc/memcached.conf file,
-# we'll just throw away all of our in-daemon output. We need to re-tie it so
-# that non-bash shells will not hang on logout. Thanks to Michael Renner for
-# the tip
-my $fd_reopened = "/dev/null";
-
-sub handle_logfile
-{
- my ($logfile) = @_;
- $fd_reopened = $logfile;
-}
-
-sub reopen_logfile
-{
- my ($logfile) = @_;
-
- open *STDERR, ">>$logfile";
- open *STDOUT, ">>$logfile";
- open *STDIN, ">>/dev/null";
- $fd_reopened = $logfile;
-}
-
-# This is set up in place here to support other non -[a-z] directives
-
-my $conf_directives = {
- "logfile" => \&handle_logfile,
-};
-
-if(open $etchandle, $etcfile)
-{
- foreach my $line (<$etchandle>)
- {
- $line ||= "";
- $line =~ s/\#.*//g;
- $line =~ s/\s+$//g;
- $line =~ s/^\s+//g;
- next unless $line;
- next if $line =~ /^\-[dh]/;
-
- if($line =~ /^[^\-]/)
- {
- my ($directive, $arg) = $line =~ /^(.*?)\s+(.*)/;
- $conf_directives->{$directive}->($arg);
- next;
- }
-
- push @$params, $line;
- }
-
-}else{
- $params = [];
-}
-
-push @$params, "-u root" unless(grep "-u", @$params);
-$params = join " ", @$params;
-
-if(-e $pidfile)
-{
- open PIDHANDLE, "$pidfile";
- my $localpid = <PIDHANDLE>;
- close PIDHANDLE;
-
- chomp $localpid;
- if(-d "/proc/$localpid")
- {
- print STDERR "memcached is already running.\n";
- exit;
- }else{
- `rm -f $localpid`;
- }
-
-}
-
-my $pid = fork();
-
-if($pid == 0)
-{
- # setsid makes us the session leader
- setsid();
- reopen_logfile($fd_reopened);
- # must fork again now that tty is closed
- $pid = fork();
- if ($pid) {
- if(open PIDHANDLE,">$pidfile")
- {
- print PIDHANDLE $pid;
- close PIDHANDLE;
- }else{
-
- print STDERR "Can't write pidfile to $pidfile.\n";
- }
- exit(0);
- }
- exec "$memcached $params";
- exit(0);
-
-}
+++ /dev/null
-#include <stdio.h>
-
-#include "memcached.h"
-
-static void display(const char *name, size_t size) {
- printf("%s\t%d\n", name, (int)size);
-}
-
-int main(int argc, char **argv) {
-
- display("Slab Stats", sizeof(struct slab_stats));
- display("Thread stats",
- sizeof(struct thread_stats)
- - (200 * sizeof(struct slab_stats)));
- display("Global stats", sizeof(struct stats));
- display("Settings", sizeof(struct settings));
- display("Item (no cas)", sizeof(item));
- display("Item (cas)", sizeof(item) + sizeof(uint64_t));
- display("Libevent thread",
- sizeof(LIBEVENT_THREAD) - sizeof(struct thread_stats));
- display("Connection", sizeof(conn));
-
- printf("----------------------------------------\n");
-
- display("libevent thread cumulative", sizeof(LIBEVENT_THREAD));
- display("Thread stats cumulative\t", sizeof(struct thread_stats));
-
- return 0;
-}
+++ /dev/null
-/* -*- 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 <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/signal.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <pthread.h>
-
-/* 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 */
-
- unsigned int killing; /* index+1 of dying slab, or zero if none */
- 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;
-static int power_largest;
-
-static void *mem_base = NULL;
-static void *mem_current = NULL;
-static size_t mem_avail = 0;
-
-/**
- * Access to the slab allocator is protected by this lock
- */
-static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t slabs_rebalance_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/*
- * Forward Declarations
- */
-static int do_slabs_newslab(const unsigned int id);
-static void *memory_allocate(size_t size);
-static void do_slabs_free(void *ptr, const size_t size, unsigned int id);
-
-/* Preallocate as many slab pages as possible (called from slabs_init)
- on start-up, so users don't get confused out-of-memory errors when
- they do have free (in-slab) space, but no space to make new slabs.
- if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all
- slab types can be made. if max memory is less than 18 MB, only the
- smaller ones will be made. */
-static void slabs_preallocate (const unsigned int maxslabs);
-
-/*
- * Figures out which slab class (chunk size) is required to store an item of
- * a given size.
- *
- * Given object size, return id to use when allocating/freeing memory for object
- * 0 means error: can't store such a large object
- */
-
-unsigned int slabs_clsid(const size_t size) {
- int res = POWER_SMALLEST;
-
- if (size == 0)
- return 0;
- while (size > slabclass[res].size)
- if (res++ == power_largest) /* won't fit in the biggest slab */
- return 0;
- return res;
-}
-
-/**
- * Determines the chunk sizes and initializes the slab class descriptors
- * accordingly.
- */
-void slabs_init(const size_t limit, const double factor, const bool prealloc) {
- int i = POWER_SMALLEST - 1;
- unsigned int size = sizeof(item) + settings.chunk_size;
-
- mem_limit = limit;
-
- if (prealloc) {
- /* Allocate everything in a big chunk with malloc */
- mem_base = malloc(mem_limit);
- if (mem_base != NULL) {
- mem_current = mem_base;
- mem_avail = mem_limit;
- } else {
- fprintf(stderr, "Warning: Failed to allocate requested memory in"
- " one large chunk.\nWill allocate in smaller chunks\n");
- }
- }
-
- memset(slabclass, 0, sizeof(slabclass));
-
- while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
- /* Make sure items are always n-byte aligned */
- if (size % CHUNK_ALIGN_BYTES)
- size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
-
- slabclass[i].size = size;
- slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
- size *= factor;
- if (settings.verbose > 1) {
- fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
- i, slabclass[i].size, slabclass[i].perslab);
- }
- }
-
- power_largest = i;
- slabclass[power_largest].size = settings.item_size_max;
- slabclass[power_largest].perslab = 1;
- if (settings.verbose > 1) {
- fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
- i, slabclass[i].size, slabclass[i].perslab);
- }
-
- /* for the test suite: faking of how much we've already malloc'd */
- {
- char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
- if (t_initial_malloc) {
- mem_malloced = (size_t)atol(t_initial_malloc);
- }
-
- }
-
- if (prealloc) {
- slabs_preallocate(power_largest);
- }
-}
-
-static void slabs_preallocate (const unsigned int maxslabs) {
- int i;
- unsigned int prealloc = 0;
-
- /* pre-allocate a 1MB slab in every size class so people don't get
- confused by non-intuitive "SERVER_ERROR out of memory"
- messages. this is the most common question on the mailing
- list. if you really don't want this, you can rebuild without
- these three lines. */
-
- for (i = POWER_SMALLEST; i <= POWER_LARGEST; 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;
- }
-}
-
-static int do_slabs_newslab(const unsigned int id) {
- slabclass_t *p = &slabclass[id];
- int len = settings.slab_reassign ? settings.item_size_max
- : p->size * p->perslab;
- char *ptr;
-
- if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
- (grow_slab_list(id) == 0) ||
- ((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;
- mem_malloced += len;
- MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
-
- return 1;
-}
-
-/*@null@*/
-static void *do_slabs_alloc(const size_t size, unsigned int id) {
- 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);
-
- /* 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 || do_slabs_newslab(id) != 0)) {
- /* We don't have more memory available */
- ret = NULL;
- } else if (p->sl_curr != 0) {
- /* return off our freelist */
- it = (item *)p->slots;
- p->slots = it->next;
- if (it->next) it->next->prev = 0;
- p->sl_curr--;
- ret = (void *)it;
- }
-
- if (ret) {
- p->requested += size;
- MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
- } else {
- MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
- }
-
- return ret;
-}
-
-static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
- slabclass_t *p;
- item *it;
-
- assert(((item *)ptr)->slabs_clsid == 0);
- assert(id >= POWER_SMALLEST && id <= power_largest);
- if (id < POWER_SMALLEST || id > power_largest)
- return;
-
- MEMCACHED_SLABS_FREE(size, id, ptr);
- p = &slabclass[id];
-
- it = (item *)ptr;
- it->it_flags |= ITEM_SLABBED;
- it->prev = 0;
- it->next = p->slots;
- if (it->next) it->next->prev = it;
- p->slots = it;
-
- p->sl_curr++;
- p->requested -= size;
- return;
-}
-
-static int nz_strcmp(int nzlength, const char *nz, const char *z) {
- int zlength=strlen(z);
- return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1;
-}
-
-bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) {
- bool ret = true;
-
- if (add_stats != NULL) {
- if (!stat_type) {
- /* prepare general statistics for the engine */
- STATS_LOCK();
- APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes);
- APPEND_STAT("curr_items", "%u", stats.curr_items);
- APPEND_STAT("total_items", "%u", stats.total_items);
- APPEND_STAT("evictions", "%llu",
- (unsigned long long)stats.evictions);
- APPEND_STAT("reclaimed", "%llu",
- (unsigned long long)stats.reclaimed);
- STATS_UNLOCK();
- } else if (nz_strcmp(nkey, stat_type, "items") == 0) {
- item_stats(add_stats, c);
- } else if (nz_strcmp(nkey, stat_type, "slabs") == 0) {
- slabs_stats(add_stats, c);
- } else if (nz_strcmp(nkey, stat_type, "sizes") == 0) {
- item_stats_sizes(add_stats, c);
- } else {
- ret = false;
- }
- } else {
- ret = false;
- }
-
- return ret;
-}
-
-/*@null@*/
-static void do_slabs_stats(ADD_STAT add_stats, void *c) {
- int i, total;
- /* Get the per-thread stats which contain some interesting aggregates */
- struct thread_stats thread_stats;
- threadlocal_stats_aggregate(&thread_stats);
-
- total = 0;
- for(i = POWER_SMALLEST; i <= power_largest; i++) {
- slabclass_t *p = &slabclass[i];
- if (p->slabs != 0) {
- uint32_t perslab, slabs;
- slabs = p->slabs;
- perslab = p->perslab;
-
- char key_str[STAT_KEY_LEN];
- char val_str[STAT_VAL_LEN];
- int klen = 0, vlen = 0;
-
- APPEND_NUM_STAT(i, "chunk_size", "%u", p->size);
- APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab);
- APPEND_NUM_STAT(i, "total_pages", "%u", slabs);
- APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab);
- APPEND_NUM_STAT(i, "used_chunks", "%u",
- slabs*perslab - p->sl_curr);
- APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr);
- /* Stat is dead, but displaying zero instead of removing it. */
- APPEND_NUM_STAT(i, "free_chunks_end", "%u", 0);
- APPEND_NUM_STAT(i, "mem_requested", "%llu",
- (unsigned long long)p->requested);
- APPEND_NUM_STAT(i, "get_hits", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].get_hits);
- APPEND_NUM_STAT(i, "cmd_set", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].set_cmds);
- APPEND_NUM_STAT(i, "delete_hits", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].delete_hits);
- APPEND_NUM_STAT(i, "incr_hits", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].incr_hits);
- APPEND_NUM_STAT(i, "decr_hits", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].decr_hits);
- APPEND_NUM_STAT(i, "cas_hits", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].cas_hits);
- APPEND_NUM_STAT(i, "cas_badval", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].cas_badval);
- APPEND_NUM_STAT(i, "touch_hits", "%llu",
- (unsigned long long)thread_stats.slab_stats[i].touch_hits);
- total++;
- }
- }
-
- /* add overall slab stats and append terminator */
-
- APPEND_STAT("active_slabs", "%d", total);
- APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced);
- add_stats(NULL, 0, NULL, 0, c);
-}
-
-static void *memory_allocate(size_t size) {
- void *ret;
-
- if (mem_base == NULL) {
- /* We are not using a preallocated large memory chunk */
- ret = malloc(size);
- } else {
- ret = mem_current;
-
- if (size > mem_avail) {
- return NULL;
- }
-
- /* mem_current pointer _must_ be aligned!!! */
- if (size % CHUNK_ALIGN_BYTES) {
- size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
- }
-
- mem_current = ((char*)mem_current) + size;
- if (size < mem_avail) {
- mem_avail -= size;
- } else {
- mem_avail = 0;
- }
- }
-
- return ret;
-}
-
-void *slabs_alloc(size_t size, unsigned int id) {
- void *ret;
-
- pthread_mutex_lock(&slabs_lock);
- ret = do_slabs_alloc(size, id);
- pthread_mutex_unlock(&slabs_lock);
- return ret;
-}
-
-void slabs_free(void *ptr, size_t size, unsigned int id) {
- pthread_mutex_lock(&slabs_lock);
- do_slabs_free(ptr, size, id);
- pthread_mutex_unlock(&slabs_lock);
-}
-
-void slabs_stats(ADD_STAT add_stats, void *c) {
- pthread_mutex_lock(&slabs_lock);
- do_slabs_stats(add_stats, c);
- pthread_mutex_unlock(&slabs_lock);
-}
-
-void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal)
-{
- pthread_mutex_lock(&slabs_lock);
- slabclass_t *p;
- if (id < POWER_SMALLEST || id > power_largest) {
- fprintf(stderr, "Internal error! Invalid slab class\n");
- abort();
- }
-
- p = &slabclass[id];
- p->requested = p->requested - old + ntotal;
- pthread_mutex_unlock(&slabs_lock);
-}
-
-static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER;
-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(&cache_lock);
- pthread_mutex_lock(&slabs_lock);
-
- if (slab_rebal.s_clsid < POWER_SMALLEST ||
- slab_rebal.s_clsid > power_largest ||
- slab_rebal.d_clsid < POWER_SMALLEST ||
- 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);
- pthread_mutex_unlock(&cache_lock);
- return no_go; /* Should use a wrapper function... */
- }
-
- s_cls->killing = 1;
-
- slab_rebal.slab_start = s_cls->slab_list[s_cls->killing - 1];
- slab_rebal.slab_end = (char *)slab_rebal.slab_start +
- (s_cls->size * s_cls->perslab);
- slab_rebal.slab_pos = slab_rebal.slab_start;
- slab_rebal.done = 0;
-
- /* Also tells do_item_get to search for items in this slab */
- slab_rebalance_signal = 2;
-
- if (settings.verbose > 1) {
- fprintf(stderr, "Started a slab rebalance\n");
- }
-
- pthread_mutex_unlock(&slabs_lock);
- pthread_mutex_unlock(&cache_lock);
-
- STATS_LOCK();
- stats.slab_reassign_running = true;
- STATS_UNLOCK();
-
- return 0;
-}
-
-enum move_status {
- MOVE_PASS=0, MOVE_DONE, MOVE_BUSY
-};
-
-/* refcount == 0 is safe since nobody can incr while cache_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
- */
-static int slab_rebalance_move(void) {
- slabclass_t *s_cls;
- int x;
- int was_busy = 0;
- int refcount = 0;
- enum move_status status = MOVE_PASS;
-
- pthread_mutex_lock(&cache_lock);
- pthread_mutex_lock(&slabs_lock);
-
- s_cls = &slabclass[slab_rebal.s_clsid];
-
- for (x = 0; x < slab_bulk_check; x++) {
- item *it = slab_rebal.slab_pos;
- status = MOVE_PASS;
- if (it->slabs_clsid != 255) {
- refcount = refcount_incr(&it->refcount);
- if (refcount == 1) { /* item is unlinked, unused */
- if (it->it_flags & ITEM_SLABBED) {
- /* remove from slab freelist */
- if (s_cls->slots == it) {
- s_cls->slots = it->next;
- }
- if (it->next) it->next->prev = it->prev;
- if (it->prev) it->prev->next = it->next;
- s_cls->sl_curr--;
- status = MOVE_DONE;
- } else {
- status = MOVE_BUSY;
- }
- } else if (refcount == 2) { /* item is linked but not busy */
- if ((it->it_flags & ITEM_LINKED) != 0) {
- do_item_unlink_nolock(it, hash(ITEM_key(it), it->nkey, 0));
- status = MOVE_DONE;
- } else {
- /* refcount == 1 + !ITEM_LINKED means the item is being
- * uploaded to, or was just unlinked but hasn't been freed
- * yet. Let it bleed off on its own and try again later */
- status = MOVE_BUSY;
- }
- } else {
- if (settings.verbose > 2) {
- fprintf(stderr, "Slab reassign hit a busy item: refcount: %d (%d -> %d)\n",
- it->refcount, slab_rebal.s_clsid, slab_rebal.d_clsid);
- }
- status = MOVE_BUSY;
- }
- }
-
- switch (status) {
- case MOVE_DONE:
- it->refcount = 0;
- it->it_flags = 0;
- it->slabs_clsid = 255;
- break;
- case MOVE_BUSY:
- slab_rebal.busy_items++;
- was_busy++;
- refcount_decr(&it->refcount);
- 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;
- slab_rebal.busy_items = 0;
- } else {
- slab_rebal.done++;
- }
- }
-
- pthread_mutex_unlock(&slabs_lock);
- pthread_mutex_unlock(&cache_lock);
-
- return was_busy;
-}
-
-static void slab_rebalance_finish(void) {
- slabclass_t *s_cls;
- slabclass_t *d_cls;
-
- pthread_mutex_lock(&cache_lock);
- pthread_mutex_lock(&slabs_lock);
-
- s_cls = &slabclass[slab_rebal.s_clsid];
- d_cls = &slabclass[slab_rebal.d_clsid];
-
- /* At this point the stolen slab is completely clear */
- s_cls->slab_list[s_cls->killing - 1] =
- s_cls->slab_list[s_cls->slabs - 1];
- s_cls->slabs--;
- s_cls->killing = 0;
-
- memset(slab_rebal.slab_start, 0, (size_t)settings.item_size_max);
-
- d_cls->slab_list[d_cls->slabs++] = slab_rebal.slab_start;
- split_slab_page_into_freelist(slab_rebal.slab_start,
- slab_rebal.d_clsid);
-
- slab_rebal.done = 0;
- slab_rebal.s_clsid = 0;
- slab_rebal.d_clsid = 0;
- slab_rebal.slab_start = NULL;
- slab_rebal.slab_end = NULL;
- slab_rebal.slab_pos = NULL;
-
- slab_rebalance_signal = 0;
-
- pthread_mutex_unlock(&slabs_lock);
- pthread_mutex_unlock(&cache_lock);
-
- STATS_LOCK();
- stats.slab_reassign_running = false;
- stats.slabs_moved++;
- STATS_UNLOCK();
-
- if (settings.verbose > 1) {
- fprintf(stderr, "finished a slab move\n");
- }
-}
-
-/* Return 1 means a decision was reached.
- * Move to its own thread (created/destroyed as needed) once automover is more
- * complex.
- */
-static int slab_automove_decision(int *src, int *dst) {
- static uint64_t evicted_old[POWER_LARGEST];
- static unsigned int slab_zeroes[POWER_LARGEST];
- static unsigned int slab_winner = 0;
- static unsigned int slab_wins = 0;
- uint64_t evicted_new[POWER_LARGEST];
- uint64_t evicted_diff = 0;
- uint64_t evicted_max = 0;
- unsigned int highest_slab = 0;
- unsigned int total_pages[POWER_LARGEST];
- int i;
- int source = 0;
- int dest = 0;
- static rel_time_t next_run;
-
- /* Run less frequently than the slabmove tester. */
- if (current_time >= next_run) {
- next_run = current_time + 10;
- } else {
- return 0;
- }
-
- item_stats_evictions(evicted_new);
- pthread_mutex_lock(&cache_lock);
- for (i = POWER_SMALLEST; i < power_largest; i++) {
- total_pages[i] = slabclass[i].slabs;
- }
- pthread_mutex_unlock(&cache_lock);
-
- /* Find a candidate source; something with zero evicts 3+ times */
- for (i = POWER_SMALLEST; i < power_largest; i++) {
- evicted_diff = evicted_new[i] - evicted_old[i];
- if (evicted_diff == 0 && total_pages[i] > 2) {
- slab_zeroes[i]++;
- if (source == 0 && slab_zeroes[i] >= 3)
- source = i;
- } else {
- slab_zeroes[i] = 0;
- if (evicted_diff > evicted_max) {
- evicted_max = evicted_diff;
- highest_slab = i;
- }
- }
- evicted_old[i] = evicted_new[i];
- }
-
- /* Pick a valid destination */
- if (slab_winner != 0 && slab_winner == highest_slab) {
- slab_wins++;
- if (slab_wins >= 3)
- dest = slab_winner;
- } else {
- slab_wins = 1;
- slab_winner = highest_slab;
- }
-
- if (source && dest) {
- *src = source;
- *dst = dest;
- return 1;
- }
- return 0;
-}
-
-/* Slab rebalancer thread.
- * Does not use spinlocks since it is not timing sensitive. Burn less CPU and
- * go to sleep if locks are contended
- */
-static void *slab_maintenance_thread(void *arg) {
- int src, dest;
-
- while (do_run_slab_thread) {
- if (settings.slab_automove == 1) {
- if (slab_automove_decision(&src, &dest) == 1) {
- /* Blind to the return codes. It will retry on its own */
- slabs_reassign(src, dest);
- }
- sleep(1);
- } else {
- /* Don't wake as often if we're not enabled.
- * This is lazier than setting up a condition right now. */
- sleep(5);
- }
- }
- return NULL;
-}
-
-/* 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;
-
- while (do_run_slab_rebalance_thread) {
- if (slab_rebalance_signal == 1) {
- if (slab_rebalance_start() < 0) {
- /* Handle errors with more specifity as required. */
- slab_rebalance_signal = 0;
- }
-
- was_busy = 0;
- } else if (slab_rebalance_signal && slab_rebal.slab_start != NULL) {
- was_busy = slab_rebalance_move();
- }
-
- if (slab_rebal.done) {
- slab_rebalance_finish();
- } else if (was_busy) {
- /* Stuck waiting for some items to unlock, so slow down a bit
- * to give them a chance to free up */
- usleep(50);
- }
-
- if (slab_rebalance_signal == 0) {
- /* always hold this lock while we're running */
- pthread_cond_wait(&slab_rebalance_cond, &slabs_rebalance_lock);
- }
- }
- return NULL;
-}
-
-/* Iterate at most once through the slab classes and pick a "random" source.
- * I like this better than calling rand() since rand() is slow enough that we
- * can just check all of the classes once instead.
- */
-static int slabs_reassign_pick_any(int dst) {
- static int cur = POWER_SMALLEST - 1;
- int tries = power_largest - POWER_SMALLEST + 1;
- for (; tries > 0; tries--) {
- cur++;
- if (cur > power_largest)
- cur = POWER_SMALLEST;
- if (cur == dst)
- continue;
- if (slabclass[cur].slabs > 1) {
- return cur;
- }
- }
- return -1;
-}
-
-static enum reassign_result_type do_slabs_reassign(int src, int dst) {
- if (slab_rebalance_signal != 0)
- return REASSIGN_RUNNING;
-
- if (src == dst)
- return REASSIGN_SRC_DST_SAME;
-
- /* Special indicator to choose ourselves. */
- if (src == -1) {
- src = slabs_reassign_pick_any(dst);
- /* TODO: If we end up back at -1, return a new error type */
- }
-
- if (src < POWER_SMALLEST || src > power_largest ||
- dst < POWER_SMALLEST || dst > power_largest)
- return REASSIGN_BADCLASS;
-
- if (slabclass[src].slabs < 2)
- return REASSIGN_NOSPARE;
-
- slab_rebal.s_clsid = src;
- slab_rebal.d_clsid = dst;
-
- slab_rebalance_signal = 1;
- pthread_cond_signal(&slab_rebalance_cond);
-
- return REASSIGN_OK;
-}
-
-enum reassign_result_type slabs_reassign(int src, int dst) {
- enum reassign_result_type ret;
- if (pthread_mutex_trylock(&slabs_rebalance_lock) != 0) {
- return REASSIGN_RUNNING;
- }
- ret = do_slabs_reassign(src, dst);
- pthread_mutex_unlock(&slabs_rebalance_lock);
- return ret;
-}
-
-static pthread_t maintenance_tid;
-static pthread_t rebalance_tid;
-
-int start_slab_maintenance_thread(void) {
- int ret;
- slab_rebalance_signal = 0;
- slab_rebal.slab_start = NULL;
- char *env = getenv("MEMCACHED_SLAB_BULK_CHECK");
- if (env != NULL) {
- slab_bulk_check = atoi(env);
- if (slab_bulk_check == 0) {
- slab_bulk_check = DEFAULT_SLAB_BULK_CHECK;
- }
- }
-
- if (pthread_cond_init(&slab_rebalance_cond, NULL) != 0) {
- fprintf(stderr, "Can't intiialize rebalance condition\n");
- return -1;
- }
- pthread_mutex_init(&slabs_rebalance_lock, NULL);
-
- if ((ret = pthread_create(&maintenance_tid, NULL,
- slab_maintenance_thread, NULL)) != 0) {
- fprintf(stderr, "Can't create slab maint thread: %s\n", strerror(ret));
- return -1;
- }
- 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;
-}
-
-void stop_slab_maintenance_thread(void) {
- mutex_lock(&cache_lock);
- do_run_slab_thread = 0;
- do_run_slab_rebalance_thread = 0;
- pthread_cond_signal(&maintenance_cond);
- pthread_mutex_unlock(&cache_lock);
-
- /* Wait for the maintenance thread to stop */
- pthread_join(maintenance_tid, NULL);
- pthread_join(rebalance_tid, NULL);
-}
+++ /dev/null
-/* slabs memory allocation */
-#ifndef SLABS_H
-#define SLABS_H
-
-/** Init the subsystem. 1st argument is the limit on no. of bytes to allocate,
- 0 if no limit. 2nd argument is the growth factor; each slab will use a chunk
- size equal to the previous slab's chunk size times this factor.
- 3rd argument specifies if the slab allocator should allocate all memory
- up front (if true), or allocate memory in chunks as it is needed (if false)
-*/
-void slabs_init(const size_t limit, const double factor, const bool prealloc);
-
-
-/**
- * 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);
-
-/** Allocate object of given length. 0 on error */ /*@null@*/
-void *slabs_alloc(const size_t size, unsigned int id);
-
-/** Free previously allocated object */
-void slabs_free(void *ptr, size_t size, unsigned int id);
-
-/** Adjust the stats for memory requested */
-void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal);
-
-/** Return a datum for stats in binary protocol */
-bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c);
-
-/** Fill buffer with stats */ /*@null@*/
-void slabs_stats(ADD_STAT add_stats, void *c);
-
-int start_slab_maintenance_thread(void);
-void stop_slab_maintenance_thread(void);
-
-enum reassign_result_type {
- REASSIGN_OK=0, REASSIGN_RUNNING, REASSIGN_BADCLASS, REASSIGN_NOSPARE,
- REASSIGN_SRC_DST_SAME
-};
-
-enum reassign_result_type slabs_reassign(int src, int dst);
-
-#endif
+++ /dev/null
-#include <stdlib.h>
-#include <priv.h>
-#include <stdio.h>
-#include "memcached.h"
-
-/*
- * this section of code will drop all (Solaris) 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 privileges(5) for more information.
- */
-void drop_privileges(void) {
- priv_set_t *privs = priv_str_to_set("basic", ",", NULL);
-
- if (privs == NULL) {
- perror("priv_str_to_set");
- exit(EXIT_FAILURE);
- }
-
- (void)priv_delset(privs, PRIV_FILE_LINK_ANY);
- (void)priv_delset(privs, PRIV_PROC_EXEC);
- (void)priv_delset(privs, PRIV_PROC_FORK);
- (void)priv_delset(privs, PRIV_PROC_INFO);
- (void)priv_delset(privs, PRIV_PROC_SESSION);
-
- if (setppriv(PRIV_SET, PRIV_PERMITTED, privs) != 0) {
- perror("setppriv(PRIV_SET, PRIV_PERMITTED)");
- exit(EXIT_FAILURE);
- }
-
- priv_emptyset(privs);
-
- if (setppriv(PRIV_SET, PRIV_INHERITABLE, privs) != 0) {
- perror("setppriv(PRIV_SET, PRIV_INHERITABLE)");
- exit(EXIT_FAILURE);
- }
-
- if (setppriv(PRIV_SET, PRIV_LIMIT, privs) != 0) {
- perror("setppriv(PRIV_SET, PRIV_LIMIT)");
- exit(EXIT_FAILURE);
- }
-
- priv_freeset(privs);
-}
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- * Detailed statistics management. For simple stats like total number of
- * "get" requests, we use inline code in memcached.c and friends, but when
- * stats detail mode is activated, the code here records more information.
- *
- * Author:
- * Steven Grimm <sgrimm@facebook.com>
- */
-#include "memcached.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-/*
- * Stats are tracked on the basis of key prefixes. This is a simple
- * fixed-size hash of prefixes; we run the prefixes through the same
- * CRC function used by the cache hashtable.
- */
-typedef struct _prefix_stats PREFIX_STATS;
-struct _prefix_stats {
- char *prefix;
- size_t prefix_len;
- uint64_t num_gets;
- uint64_t num_sets;
- uint64_t num_deletes;
- uint64_t num_hits;
- PREFIX_STATS *next;
-};
-
-#define PREFIX_HASH_SIZE 256
-
-static PREFIX_STATS *prefix_stats[PREFIX_HASH_SIZE];
-static int num_prefixes = 0;
-static int total_prefix_size = 0;
-
-void stats_prefix_init() {
- memset(prefix_stats, 0, sizeof(prefix_stats));
-}
-
-/*
- * Cleans up all our previously collected stats. NOTE: the stats lock is
- * assumed to be held when this is called.
- */
-void stats_prefix_clear() {
- int i;
-
- for (i = 0; i < PREFIX_HASH_SIZE; i++) {
- PREFIX_STATS *cur, *next;
- for (cur = prefix_stats[i]; cur != NULL; cur = next) {
- next = cur->next;
- free(cur->prefix);
- free(cur);
- }
- prefix_stats[i] = NULL;
- }
- num_prefixes = 0;
- total_prefix_size = 0;
-}
-
-/*
- * Returns the stats structure for a prefix, creating it if it's not already
- * in the list.
- */
-/*@null@*/
-static PREFIX_STATS *stats_prefix_find(const char *key, const size_t nkey) {
- PREFIX_STATS *pfs;
- uint32_t hashval;
- size_t length;
- bool bailout = true;
-
- assert(key != NULL);
-
- for (length = 0; length < nkey && key[length] != '\0'; length++) {
- if (key[length] == settings.prefix_delimiter) {
- bailout = false;
- break;
- }
- }
-
- if (bailout) {
- return NULL;
- }
-
- hashval = hash(key, length, 0) % PREFIX_HASH_SIZE;
-
- for (pfs = prefix_stats[hashval]; NULL != pfs; pfs = pfs->next) {
- if (strncmp(pfs->prefix, key, length) == 0)
- return pfs;
- }
-
- pfs = calloc(sizeof(PREFIX_STATS), 1);
- if (NULL == pfs) {
- perror("Can't allocate space for stats structure: calloc");
- return NULL;
- }
-
- pfs->prefix = malloc(length + 1);
- if (NULL == pfs->prefix) {
- perror("Can't allocate space for copy of prefix: malloc");
- free(pfs);
- return NULL;
- }
-
- strncpy(pfs->prefix, key, length);
- pfs->prefix[length] = '\0'; /* because strncpy() sucks */
- pfs->prefix_len = length;
-
- pfs->next = prefix_stats[hashval];
- prefix_stats[hashval] = pfs;
-
- num_prefixes++;
- total_prefix_size += length;
-
- return pfs;
-}
-
-/*
- * Records a "get" of a key.
- */
-void stats_prefix_record_get(const char *key, const size_t nkey, const bool is_hit) {
- PREFIX_STATS *pfs;
-
- STATS_LOCK();
- pfs = stats_prefix_find(key, nkey);
- if (NULL != pfs) {
- pfs->num_gets++;
- if (is_hit) {
- pfs->num_hits++;
- }
- }
- STATS_UNLOCK();
-}
-
-/*
- * Records a "delete" of a key.
- */
-void stats_prefix_record_delete(const char *key, const size_t nkey) {
- PREFIX_STATS *pfs;
-
- STATS_LOCK();
- pfs = stats_prefix_find(key, nkey);
- if (NULL != pfs) {
- pfs->num_deletes++;
- }
- STATS_UNLOCK();
-}
-
-/*
- * Records a "set" of a key.
- */
-void stats_prefix_record_set(const char *key, const size_t nkey) {
- PREFIX_STATS *pfs;
-
- STATS_LOCK();
- pfs = stats_prefix_find(key, nkey);
- if (NULL != pfs) {
- pfs->num_sets++;
- }
- STATS_UNLOCK();
-}
-
-/*
- * Returns stats in textual form suitable for writing to a client.
- */
-/*@null@*/
-char *stats_prefix_dump(int *length) {
- const char *format = "PREFIX %s get %llu hit %llu set %llu del %llu\r\n";
- PREFIX_STATS *pfs;
- char *buf;
- int i, pos;
- size_t size = 0, written = 0, total_written = 0;
-
- /*
- * Figure out how big the buffer needs to be. This is the sum of the
- * lengths of the prefixes themselves, plus the size of one copy of
- * the per-prefix output with 20-digit values for all the counts,
- * plus space for the "END" at the end.
- */
- STATS_LOCK();
- size = strlen(format) + total_prefix_size +
- num_prefixes * (strlen(format) - 2 /* %s */
- + 4 * (20 - 4)) /* %llu replaced by 20-digit num */
- + sizeof("END\r\n");
- buf = malloc(size);
- if (NULL == buf) {
- perror("Can't allocate stats response: malloc");
- STATS_UNLOCK();
- return NULL;
- }
-
- pos = 0;
- for (i = 0; i < PREFIX_HASH_SIZE; i++) {
- for (pfs = prefix_stats[i]; NULL != pfs; pfs = pfs->next) {
- written = snprintf(buf + pos, size-pos, format,
- pfs->prefix, pfs->num_gets, pfs->num_hits,
- pfs->num_sets, pfs->num_deletes);
- pos += written;
- total_written += written;
- assert(total_written < size);
- }
- }
-
- STATS_UNLOCK();
- memcpy(buf + pos, "END\r\n", 6);
-
- *length = pos + 5;
- return buf;
-}
-
-
-#ifdef UNIT_TEST
-
-/****************************************************************************
- To run unit tests, compile with $(CC) -DUNIT_TEST stats.c assoc.o
- (need assoc.o to get the hash() function).
-****************************************************************************/
-
-struct settings settings;
-
-static char *current_test = "";
-static int test_count = 0;
-static int fail_count = 0;
-
-static void fail(char *what) { printf("\tFAIL: %s\n", what); fflush(stdout); fail_count++; }
-static void test_equals_int(char *what, int a, int b) { test_count++; if (a != b) fail(what); }
-static void test_equals_ptr(char *what, void *a, void *b) { test_count++; if (a != b) fail(what); }
-static void test_equals_str(char *what, const char *a, const char *b) { test_count++; if (strcmp(a, b)) fail(what); }
-static void test_equals_ull(char *what, uint64_t a, uint64_t b) { test_count++; if (a != b) fail(what); }
-static void test_notequals_ptr(char *what, void *a, void *b) { test_count++; if (a == b) fail(what); }
-static void test_notnull_ptr(char *what, void *a) { test_count++; if (NULL == a) fail(what); }
-
-static void test_prefix_find() {
- PREFIX_STATS *pfs1, *pfs2;
-
- pfs1 = stats_prefix_find("abc");
- test_notnull_ptr("initial prefix find", pfs1);
- test_equals_ull("request counts", 0ULL,
- pfs1->num_gets + pfs1->num_sets + pfs1->num_deletes + pfs1->num_hits);
- pfs2 = stats_prefix_find("abc");
- test_equals_ptr("find of same prefix", pfs1, pfs2);
- pfs2 = stats_prefix_find("abc:");
- test_equals_ptr("find of same prefix, ignoring delimiter", pfs1, pfs2);
- pfs2 = stats_prefix_find("abc:d");
- test_equals_ptr("find of same prefix, ignoring extra chars", pfs1, pfs2);
- pfs2 = stats_prefix_find("xyz123");
- test_notequals_ptr("find of different prefix", pfs1, pfs2);
- pfs2 = stats_prefix_find("ab:");
- test_notequals_ptr("find of shorter prefix", pfs1, pfs2);
-}
-
-static void test_prefix_record_get() {
- PREFIX_STATS *pfs;
-
- stats_prefix_record_get("abc:123", 0);
- pfs = stats_prefix_find("abc:123");
- test_equals_ull("get count after get #1", 1, pfs->num_gets);
- test_equals_ull("hit count after get #1", 0, pfs->num_hits);
- stats_prefix_record_get("abc:456", 0);
- test_equals_ull("get count after get #2", 2, pfs->num_gets);
- test_equals_ull("hit count after get #2", 0, pfs->num_hits);
- stats_prefix_record_get("abc:456", 1);
- test_equals_ull("get count after get #3", 3, pfs->num_gets);
- test_equals_ull("hit count after get #3", 1, pfs->num_hits);
- stats_prefix_record_get("def:", 1);
- test_equals_ull("get count after get #4", 3, pfs->num_gets);
- test_equals_ull("hit count after get #4", 1, pfs->num_hits);
-}
-
-static void test_prefix_record_delete() {
- PREFIX_STATS *pfs;
-
- stats_prefix_record_delete("abc:123");
- pfs = stats_prefix_find("abc:123");
- test_equals_ull("get count after delete #1", 0, pfs->num_gets);
- test_equals_ull("hit count after delete #1", 0, pfs->num_hits);
- test_equals_ull("delete count after delete #1", 1, pfs->num_deletes);
- test_equals_ull("set count after delete #1", 0, pfs->num_sets);
- stats_prefix_record_delete("def:");
- test_equals_ull("delete count after delete #2", 1, pfs->num_deletes);
-}
-
-static void test_prefix_record_set() {
- PREFIX_STATS *pfs;
-
- stats_prefix_record_set("abc:123");
- pfs = stats_prefix_find("abc:123");
- test_equals_ull("get count after set #1", 0, pfs->num_gets);
- test_equals_ull("hit count after set #1", 0, pfs->num_hits);
- test_equals_ull("delete count after set #1", 0, pfs->num_deletes);
- test_equals_ull("set count after set #1", 1, pfs->num_sets);
- stats_prefix_record_delete("def:");
- test_equals_ull("set count after set #2", 1, pfs->num_sets);
-}
-
-static void test_prefix_dump() {
- int hashval = hash("abc", 3, 0) % PREFIX_HASH_SIZE;
- char tmp[500];
- char *expected;
- int keynum;
- int length;
-
- test_equals_str("empty stats", "END\r\n", stats_prefix_dump(&length));
- test_equals_int("empty stats length", 5, length);
- stats_prefix_record_set("abc:123");
- expected = "PREFIX abc get 0 hit 0 set 1 del 0\r\nEND\r\n";
- test_equals_str("stats after set", expected, stats_prefix_dump(&length));
- test_equals_int("stats length after set", strlen(expected), length);
- stats_prefix_record_get("abc:123", 0);
- expected = "PREFIX abc get 1 hit 0 set 1 del 0\r\nEND\r\n";
- test_equals_str("stats after get #1", expected, stats_prefix_dump(&length));
- test_equals_int("stats length after get #1", strlen(expected), length);
- stats_prefix_record_get("abc:123", 1);
- expected = "PREFIX abc get 2 hit 1 set 1 del 0\r\nEND\r\n";
- test_equals_str("stats after get #2", expected, stats_prefix_dump(&length));
- test_equals_int("stats length after get #2", strlen(expected), length);
- stats_prefix_record_delete("abc:123");
- expected = "PREFIX abc get 2 hit 1 set 1 del 1\r\nEND\r\n";
- test_equals_str("stats after del #1", expected, stats_prefix_dump(&length));
- test_equals_int("stats length after del #1", strlen(expected), length);
-
- /* The order of results might change if we switch hash functions. */
- stats_prefix_record_delete("def:123");
- expected = "PREFIX abc get 2 hit 1 set 1 del 1\r\n"
- "PREFIX def get 0 hit 0 set 0 del 1\r\n"
- "END\r\n";
- test_equals_str("stats after del #2", expected, stats_prefix_dump(&length));
- test_equals_int("stats length after del #2", strlen(expected), length);
-
- /* Find a key that hashes to the same bucket as "abc" */
- for (keynum = 0; keynum < PREFIX_HASH_SIZE * 100; keynum++) {
- snprintf(tmp, sizeof(tmp), "%d", keynum);
- if (hashval == hash(tmp, strlen(tmp), 0) % PREFIX_HASH_SIZE) {
- break;
- }
- }
- stats_prefix_record_set(tmp);
- snprintf(tmp, sizeof(tmp),
- "PREFIX %d get 0 hit 0 set 1 del 0\r\n"
- "PREFIX abc get 2 hit 1 set 1 del 1\r\n"
- "PREFIX def get 0 hit 0 set 0 del 1\r\n"
- "END\r\n", keynum);
- test_equals_str("stats with two stats in one bucket",
- tmp, stats_prefix_dump(&length));
- test_equals_int("stats length with two stats in one bucket",
- strlen(tmp), length);
-}
-
-static void run_test(char *what, void (*func)(void)) {
- current_test = what;
- test_count = fail_count = 0;
- puts(what);
- fflush(stdout);
-
- stats_prefix_clear();
- (func)();
- printf("\t%d / %d pass\n", (test_count - fail_count), test_count);
-}
-
-/* In case we're compiled in thread mode */
-void mt_stats_lock() { }
-void mt_stats_unlock() { }
-
-main(int argc, char **argv) {
- stats_prefix_init();
- settings.prefix_delimiter = ':';
- run_test("stats_prefix_find", test_prefix_find);
- run_test("stats_prefix_record_get", test_prefix_record_get);
- run_test("stats_prefix_record_delete", test_prefix_record_delete);
- run_test("stats_prefix_record_set", test_prefix_record_set);
- run_test("stats_prefix_dump", test_prefix_dump);
-}
-
-#endif
+++ /dev/null
-/* 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);
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 18;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-eval {
- my $server = new_memcached();
- ok($server, "started the server");
-};
-is($@, '', 'Basic startup works');
-
-eval {
- my $server = new_memcached("-l fooble");
-};
-ok($@, "Died with illegal -l args");
-
-eval {
- my $server = new_memcached("-l 127.0.0.1");
-};
-is($@,'', "-l 127.0.0.1 works");
-
-eval {
- my $server = new_memcached('-C');
- my $stats = mem_stats($server->sock, 'settings');
- is('no', $stats->{'cas_enabled'});
-};
-is($@, '', "-C works");
-
-eval {
- my $server = new_memcached('-b 8675');
- my $stats = mem_stats($server->sock, 'settings');
- is('8675', $stats->{'tcp_backlog'});
-};
-is($@, '', "-b works");
-
-foreach my $val ('auto', 'ascii') {
- eval {
- my $server = new_memcached("-B $val");
- my $stats = mem_stats($server->sock, 'settings');
- ok($stats->{'binding_protocol'} =~ /$val/, "$val works");
- };
- is($@, '', "$val works");
-}
-
-# For the binary test, we just verify it starts since we don't have an easy bin client.
-eval {
- my $server = new_memcached("-B binary");
-};
-is($@, '', "binary works");
-
-eval {
- my $server = new_memcached("-vv -B auto");
-};
-is($@, '', "auto works");
-
-eval {
- my $server = new_memcached("-vv -B ascii");
-};
-is($@, '', "ascii works");
-
-
-# For the binary test, we just verify it starts since we don't have an easy bin client.
-eval {
- my $server = new_memcached("-vv -B binary");
-};
-is($@, '', "binary works");
-
-
-# Should blow up with something invalid.
-eval {
- my $server = new_memcached("-B http");
-};
-ok($@, "Died with illegal -B arg.");
-
-# Should not allow -t 0
-eval {
- my $server = new_memcached("-t 0");
-};
-ok($@, "Died with illegal 0 thread count");
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/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++;
-}
-
+++ /dev/null
-#!/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 => 25;
- } 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 <dustin@spy.net>
-
-# 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');
-
-# 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'}, 2, "auth commands counted");
- is ($stats{'auth_errors'}, 1, "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
-
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use Test::More tests => 3549;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-ok($server, "started the server");
-
-# Based almost 100% off testClient.py which is:
-# Copyright (c) 2007 Dustin Sallings <dustin@spy.net>
-
-# Command constants
-use constant CMD_GET => 0x00;
-use constant CMD_SET => 0x01;
-use constant CMD_ADD => 0x02;
-use constant CMD_REPLACE => 0x03;
-use constant CMD_DELETE => 0x04;
-use constant CMD_INCR => 0x05;
-use constant CMD_DECR => 0x06;
-use constant CMD_QUIT => 0x07;
-use constant CMD_FLUSH => 0x08;
-use constant CMD_GETQ => 0x09;
-use constant CMD_NOOP => 0x0A;
-use constant CMD_VERSION => 0x0B;
-use constant CMD_GETK => 0x0C;
-use constant CMD_GETKQ => 0x0D;
-use constant CMD_APPEND => 0x0E;
-use constant CMD_PREPEND => 0x0F;
-use constant CMD_STAT => 0x10;
-use constant CMD_SETQ => 0x11;
-use constant CMD_ADDQ => 0x12;
-use constant CMD_REPLACEQ => 0x13;
-use constant CMD_DELETEQ => 0x14;
-use constant CMD_INCREMENTQ => 0x15;
-use constant CMD_DECREMENTQ => 0x16;
-use constant CMD_QUITQ => 0x17;
-use constant CMD_FLUSHQ => 0x18;
-use constant CMD_APPENDQ => 0x19;
-use constant CMD_PREPENDQ => 0x1A;
-use constant CMD_TOUCH => 0x1C;
-use constant CMD_GAT => 0x1D;
-use constant CMD_GATQ => 0x1E;
-use constant CMD_GATK => 0x23;
-use constant CMD_GATKQ => 0x24;
-
-# REQ and RES formats are divided even though they currently share
-# the same format, since they _could_ differ in the future.
-use constant REQ_PKT_FMT => "CCnCCnNNNN";
-use constant RES_PKT_FMT => "CCnCCnNNNN";
-use constant INCRDECR_PKT_FMT => "NNNNN";
-use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT));
-use constant REQ_MAGIC => 0x80;
-use constant RES_MAGIC => 0x81;
-
-my $mc = MC::Client->new;
-
-# Let's turn on detail stats for all this stuff
-
-$mc->stats('detail on');
-
-my $check = sub {
- my ($key, $orig_flags, $orig_val) = @_;
- my ($flags, $val, $cas) = $mc->get($key);
- is($flags, $orig_flags, "Flags is set properly");
- ok($val eq $orig_val || $val == $orig_val, $val . " = " . $orig_val);
-};
-
-my $set = sub {
- my ($key, $exp, $orig_flags, $orig_value) = @_;
- $mc->set($key, $orig_value, $orig_flags, $exp);
- $check->($key, $orig_flags, $orig_value);
-};
-
-my $empty = sub {
- my $key = shift;
- my $rv =()= eval { $mc->get($key) };
- is($rv, 0, "Didn't get a result from get");
- ok($@->not_found, "We got a not found error when we expected one");
-};
-
-my $delete = sub {
- my ($key, $when) = @_;
- $mc->delete($key, $when);
- $empty->($key);
-};
-
-# diag "Test Version";
-my $v = $mc->version;
-ok(defined $v && length($v), "Proper version: $v");
-
-# Bug 71
-{
- my %stats1 = $mc->stats('');
- $mc->flush;
- my %stats2 = $mc->stats('');
-
- is($stats2{'cmd_flush'}, $stats1{'cmd_flush'} + 1,
- "Stats not updated on a binary flush");
-}
-
-# diag "Flushing...";
-$mc->flush;
-
-# diag "Noop";
-$mc->noop;
-
-# diag "Simple set/get";
-$set->('x', 5, 19, "somevalue");
-
-# diag "Delete";
-$delete->('x');
-
-# diag "Flush";
-$set->('x', 5, 19, "somevaluex");
-$set->('y', 5, 17, "somevaluey");
-$mc->flush;
-$empty->('x');
-$empty->('y');
-
-{
- # diag "Add";
- $empty->('i');
- $mc->add('i', 'ex', 5, 10);
- $check->('i', 5, "ex");
-
- my $rv =()= eval { $mc->add('i', "ex2", 10, 5) };
- is($rv, 0, "Add didn't return anything");
- ok($@->exists, "Expected exists error received");
- $check->('i', 5, "ex");
-}
-
-{
- # diag "Too big.";
- $empty->('toobig');
- $mc->set('toobig', 'not too big', 10, 10);
- eval {
- my $bigval = ("x" x (1024*1024)) . "x";
- $mc->set('toobig', $bigval, 10, 10);
- };
- ok($@->too_big, "Was too big");
- $empty->('toobig');
-}
-
-{
- # diag "Replace";
- $empty->('j');
-
- my $rv =()= eval { $mc->replace('j', "ex", 19, 5) };
- is($rv, 0, "Replace didn't return anything");
- ok($@->not_found, "Expected not_found error received");
- $empty->('j');
- $mc->add('j', "ex2", 14, 5);
- $check->('j', 14, "ex2");
- $mc->replace('j', "ex3", 24, 5);
- $check->('j', 24, "ex3");
-}
-
-{
- # diag "MultiGet";
- $mc->add('xx', "ex", 1, 5);
- $mc->add('wye', "why", 2, 5);
- my $rv = $mc->get_multi(qw(xx wye zed));
-
- # CAS is returned with all gets.
- $rv->{xx}->[2] = 0;
- $rv->{wye}->[2] = 0;
- is_deeply($rv->{xx}, [1, 'ex', 0], "X is correct");
- is_deeply($rv->{wye}, [2, 'why', 0], "Y is correct");
- is(keys(%$rv), 2, "Got only two answers like we expect");
-}
-
-# diag "Test increment";
-$mc->flush;
-is($mc->incr("x"), 0, "First incr call is zero");
-is($mc->incr("x"), 1, "Second incr call is one");
-is($mc->incr("x", 211), 212, "Adding 211 gives you 212");
-is($mc->incr("x", 2**33), 8589934804, "Blast the 32bit border");
-
-# diag "Issue 48 - incrementing plain text.";
-{
- $mc->set("issue48", "text", 0, 0);
- my $rv =()= eval { $mc->incr('issue48'); };
- ok($@ && $@->delta_badval, "Expected invalid value when incrementing text.");
- $check->('issue48', 0, "text");
-
- $rv =()= eval { $mc->decr('issue48'); };
- ok($@ && $@->delta_badval, "Expected invalid value when decrementing text.");
- $check->('issue48', 0, "text");
-}
-
-
-# diag "Test decrement";
-$mc->flush;
-is($mc->incr("x", undef, 5), 5, "Initial value");
-is($mc->decr("x"), 4, "Decrease by one");
-is($mc->decr("x", 211), 0, "Floor is zero");
-
-{
- # diag "bug220
- my ($rv, $cas) = $mc->set("bug220", "100", 0, 0);
- my ($irv, $icas) = $mc->incr_cas("bug220", 999);
- ok($icas != $cas);
- is($irv, 1099, "Incr amount failed");
- my ($flags, $val, $gcas) = $mc->get("bug220");
- is($gcas, $icas, "CAS didn't match after incr/gets");
-
- ($irv, $icas) = $mc->incr_cas("bug220", 999);
- ok($icas != $cas);
- is($irv, 2098, "Incr amount failed");
- ($flags, $val, $gcas) = $mc->get("bug220");
- is($gcas, $icas, "CAS didn't match after incr/gets");
-}
-
-{
- # diag "bug21";
- $mc->add("bug21", "9223372036854775807", 0, 0);
- is($mc->incr("bug21"), 9223372036854775808, "First incr for bug21.");
- is($mc->incr("bug21"), 9223372036854775809, "Second incr for bug21.");
- is($mc->decr("bug21"), 9223372036854775808, "Decr for bug21.");
-}
-
-{
- # diag "CAS";
- $mc->flush;
-
- {
- my $rv =()= eval { $mc->set("x", "bad value", 19, 5, 0x7FFFFFF) };
- is($rv, 0, "Empty return on expected failure");
- ok($@->not_found, "Error was 'not found' as expected");
- }
-
- my ($r, $rcas) = $mc->add("x", "original value", 5, 19);
-
- my ($flags, $val, $i) = $mc->get("x");
- is($val, "original value", "->gets returned proper value");
- is($rcas, $i, "Add CAS matched.");
-
- {
- my $rv =()= eval { $mc->set("x", "broken value", 19, 5, $i+1) };
- is($rv, 0, "Empty return on expected failure (1)");
- ok($@->exists, "Expected error state of 'exists' (1)");
- }
-
- ($r, $rcas) = $mc->set("x", "new value", 19, 5, $i);
-
- my ($newflags, $newval, $newi) = $mc->get("x");
- is($newval, "new value", "CAS properly overwrote value");
- is($rcas, $newi, "Get CAS matched.");
-
- {
- my $rv =()= eval { $mc->set("x", "replay value", 19, 5, $i) };
- is($rv, 0, "Empty return on expected failure (2)");
- ok($@->exists, "Expected error state of 'exists' (2)");
- }
-}
-
-# diag "Touch commands";
-{
- $mc->flush;
- $mc->set("totouch", "toast", 0, 1);
- my $res = $mc->touch("totouch", 10);
- sleep 2;
- $check->("totouch", 0, "toast");
-
- $mc->set("totouch", "toast2", 0, 1);
- my ($flags, $val, $i) = $mc->gat("totouch", 10);
- is($val, "toast2", "GAT returned correct value");
- sleep 2;
- $check->("totouch", 0, "toast2");
-
- # Test miss as well
- $mc->set("totouch", "toast3", 0, 1);
- $res = $mc->touch("totouch", 1);
- sleep 3;
- $empty->("totouch");
-}
-
-# diag "Silent set.";
-$mc->silent_mutation(::CMD_SETQ, 'silentset', 'silentsetval');
-
-# diag "Silent add.";
-$mc->silent_mutation(::CMD_ADDQ, 'silentadd', 'silentaddval');
-
-# diag "Silent replace.";
-{
- my $key = "silentreplace";
- my $extra = pack "NN", 829, 0;
- $empty->($key);
- # $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0);
- # $empty->($key);
-
- $mc->add($key, "xval", 831, 0);
- $check->($key, 831, 'xval');
-
- $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0);
- $check->($key, 829, 'somevalue');
-}
-
-# diag "Silent delete";
-{
- my $key = "silentdelete";
- $empty->($key);
- $mc->set($key, "some val", 19, 0);
- $mc->send_silent(::CMD_DELETEQ, $key, '', 772);
- $empty->($key);
-}
-
-# diag "Silent increment";
-{
- my $key = "silentincr";
- my $opaque = 98428747;
- $empty->($key);
- $mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 0, 0, 0);
- is($mc->incr($key, 0), 0, "First call is 0");
-
- $mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 8, 0, 0);
- is($mc->incr($key, 0), 8);
-}
-
-# diag "Silent decrement";
-{
- my $key = "silentdecr";
- my $opaque = 98428147;
- $empty->($key);
- $mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 0, 185, 0);
- is($mc->incr($key, 0), 185);
-
- $mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 8, 0, 0);
- is($mc->incr($key, 0), 177);
-}
-
-# diag "Silent flush";
-{
- my %stats1 = $mc->stats('');
-
- $set->('x', 5, 19, "somevaluex");
- $set->('y', 5, 17, "somevaluey");
- $mc->send_silent(::CMD_FLUSHQ, '', '', 2775256);
- $empty->('x');
- $empty->('y');
-
- my %stats2 = $mc->stats('');
- is($stats2{'cmd_flush'}, $stats1{'cmd_flush'} + 1,
- "Stats not updated on a binary quiet flush");
-}
-
-# diag "Append";
-{
- my $key = "appendkey";
- my $value = "some value";
- $set->($key, 8, 19, $value);
- $mc->_append_prepend(::CMD_APPEND, $key, " more");
- $check->($key, 19, $value . " more");
-}
-
-# diag "Prepend";
-{
- my $key = "prependkey";
- my $value = "some value";
- $set->($key, 8, 19, $value);
- $mc->_append_prepend(::CMD_PREPEND, $key, "prefixed ");
- $check->($key, 19, "prefixed " . $value);
-}
-
-# diag "Silent append";
-{
- my $key = "appendqkey";
- my $value = "some value";
- $set->($key, 8, 19, $value);
- $mc->send_silent(::CMD_APPENDQ, $key, " more", 7284492);
- $check->($key, 19, $value . " more");
-}
-
-# diag "Silent prepend";
-{
- my $key = "prependqkey";
- my $value = "some value";
- $set->($key, 8, 19, $value);
- $mc->send_silent(::CMD_PREPENDQ, $key, "prefixed ", 7284492);
- $check->($key, 19, "prefixed " . $value);
-}
-
-# diag "Leaky binary get test.";
-# # http://code.google.com/p/memcached/issues/detail?id=16
-{
- # Get a new socket so we can speak text to it.
- my $sock = $server->new_sock;
- my $max = 1024 * 1024;
- my $big = "a big value that's > .5M and < 1M. ";
- while (length($big) * 2 < $max) {
- $big = $big . $big;
- }
- my $biglen = length($big);
-
- for(1..100) {
- my $key = "some_key_$_";
- # print STDERR "Key is $key\n";
- # print $sock "set $key 0 0 $vallen\r\n$value\r\n";
- print $sock "set $key 0 0 $biglen\r\n$big\r\n";
- is(scalar <$sock>, "STORED\r\n", "stored big");
- my ($f, $v, $c) = $mc->get($key);
- }
-}
-
-# diag "Test stats settings."
-{
- my %stats = $mc->stats('settings');
-
- is(1024, $stats{'maxconns'});
- is('NULL', $stats{'domain_socket'});
- is('on', $stats{'evictions'});
- is('yes', $stats{'cas_enabled'});
-}
-
-# diag "Test quit commands.";
-{
- my $s2 = new_memcached();
- my $mc2 = MC::Client->new($s2);
- $mc2->send_command(CMD_QUITQ, '', '', 0, '', 0);
-
- # Five seconds ought to be enough to get hung up on.
- my $oldalarmt = alarm(5);
-
- # Verify we can't read anything.
- my $bytesread = -1;
- eval {
- local $SIG{'ALRM'} = sub { die "timeout" };
- my $data = "";
- $bytesread = sysread($mc2->{socket}, $data, 24),
- };
- is($bytesread, 0, "Read after quit.");
-
- # Restore signal stuff.
- alarm($oldalarmt);
-}
-
-# diag "Test protocol boundary overruns";
-{
- use List::Util qw[min];
- # Attempting some protocol overruns by toying around with the edge
- # of the data buffer at a few different sizes. This assumes the
- # boundary is at or around 2048 bytes.
- for (my $i = 1900; $i < 2100; $i++) {
- my $k = "test_key_$i";
- my $v = 'x' x $i;
- # diag "Trying $i $k";
- my $extra = pack "NN", 82, 0;
- my $data = $mc->build_command(::CMD_SETQ, $k, $v, 0, $extra, 0);
- $data .= $mc->build_command(::CMD_SETQ, "alt_$k", "blah", 0, $extra, 0);
- if (length($data) > 2024) {
- for (my $j = 2024; $j < min(2096, length($data)); $j++) {
- $mc->{socket}->send(substr($data, 0, $j));
- $mc->flush_socket;
- sleep(0.001);
- $mc->{socket}->send(substr($data, $j));
- $mc->flush_socket;
- }
- } else {
- $mc->{socket}->send($data);
- }
- $mc->flush_socket;
- $check->($k, 82, $v);
- $check->("alt_$k", 82, "blah");
- }
-}
-
-# Along with the assertion added to the code to verify we're staying
-# within bounds when we do a stats detail dump (detail turned on at
-# the top).
-my %stats = $mc->stats('detail dump');
-
-# This test causes a disconnection.
-{
- # diag "Key too large.";
- my $key = "x" x 365;
- eval {
- $mc->get($key, 'should die', 10, 10);
- };
- ok($@->einval, "Invalid key length");
-}
-
-# ######################################################################
-# Test ends around here.
-# ######################################################################
-
-package MC::Client;
-
-use strict;
-use warnings;
-use fields qw(socket);
-use IO::Socket::INET;
-
-sub new {
- my $self = shift;
- my ($s) = @_;
- $s = $server unless defined $s;
- my $sock = $s->sock;
- $self = fields::new($self);
- $self->{socket} = $sock;
- return $self;
-}
-
-sub build_command {
- my $self = shift;
- die "Not enough args to send_command" unless @_ >= 4;
- my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
-
- $extra_header = '' unless defined $extra_header;
- my $keylen = length($key);
- my $vallen = length($val);
- my $extralen = length($extra_header);
- my $datatype = 0; # field for future use
- my $reserved = 0; # field for future use
- my $totallen = $keylen + $vallen + $extralen;
- my $ident_hi = 0;
- my $ident_lo = 0;
-
- if ($cas) {
- $ident_hi = int($cas / 2 ** 32);
- $ident_lo = int($cas % 2 ** 32);
- }
-
- my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen,
- $datatype, $reserved, $totallen, $opaque, $ident_hi,
- $ident_lo);
- my $full_msg = $msg . $extra_header . $key . $val;
- return $full_msg;
-}
-
-sub send_command {
- my $self = shift;
- die "Not enough args to send_command" unless @_ >= 4;
- my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
-
- my $full_msg = $self->build_command($cmd, $key, $val, $opaque, $extra_header, $cas);
-
- my $sent = $self->{socket}->send($full_msg);
- die("Send failed: $!") unless $sent;
- if($sent != length($full_msg)) {
- die("only sent $sent of " . length($full_msg) . " bytes");
- }
-}
-
-sub flush_socket {
- my $self = shift;
- $self->{socket}->flush;
-}
-
-# Send a silent command and ensure it doesn't respond.
-sub send_silent {
- my $self = shift;
- die "Not enough args to send_silent" unless @_ >= 4;
- my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
-
- $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
- $self->send_command(::CMD_NOOP, '', '', $opaque + 1);
-
- my ($ropaque, $data) = $self->_handle_single_response;
- Test::More::is($ropaque, $opaque + 1);
-}
-
-sub silent_mutation {
- my $self = shift;
- my ($cmd, $key, $value) = @_;
-
- $empty->($key);
- my $extra = pack "NN", 82, 0;
- $mc->send_silent($cmd, $key, $value, 7278552, $extra, 0);
- $check->($key, 82, $value);
-}
-
-sub _handle_single_response {
- my $self = shift;
- my $myopaque = shift;
-
- my $hdr = "";
- while(::MIN_RECV_BYTES - length($hdr) > 0) {
- $self->{socket}->recv(my $response, ::MIN_RECV_BYTES - length($hdr));
- $hdr .= $response;
- }
- Test::More::is(length($hdr), ::MIN_RECV_BYTES, "Expected read length");
-
- my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining,
- $opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $hdr);
- Test::More::is($magic, ::RES_MAGIC, "Got proper response magic");
-
- my $cas = ($ident_hi * 2 ** 32) + $ident_lo;
-
- return ($opaque, '', $cas, 0) if($remaining == 0);
-
- # fetch the value
- my $rv="";
- while($remaining - length($rv) > 0) {
- $self->{socket}->recv(my $buf, $remaining - length($rv));
- $rv .= $buf;
- }
- if(length($rv) != $remaining) {
- my $found = length($rv);
- die("Expected $remaining bytes, got $found");
- }
-
- if (defined $myopaque) {
- Test::More::is($opaque, $myopaque, "Expected opaque");
- } else {
- Test::More::pass("Implicit pass since myopaque is undefined");
- }
-
- if ($status) {
- die MC::Error->new($status, $rv);
- }
-
- return ($opaque, $rv, $cas, $keylen);
-}
-
-sub _do_command {
- my $self = shift;
- die unless @_ >= 3;
- my ($cmd, $key, $val, $extra_header, $cas) = @_;
-
- $extra_header = '' unless defined $extra_header;
- my $opaque = int(rand(2**32));
- $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
- my (undef, $rv, $rcas) = $self->_handle_single_response($opaque);
- return ($rv, $rcas);
-}
-
-sub _incrdecr_header {
- my $self = shift;
- my ($amt, $init, $exp) = @_;
-
- my $amt_hi = int($amt / 2 ** 32);
- my $amt_lo = int($amt % 2 ** 32);
-
- my $init_hi = int($init / 2 ** 32);
- my $init_lo = int($init % 2 ** 32);
-
- my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi,
- $init_lo, $exp);
-
- return $extra_header;
-}
-
-sub _incrdecr_cas {
- my $self = shift;
- my ($cmd, $key, $amt, $init, $exp) = @_;
-
- my ($data, $rcas) = $self->_do_command($cmd, $key, '',
- $self->_incrdecr_header($amt, $init, $exp));
-
- my $header = substr $data, 0, 8, '';
- my ($resp_hi, $resp_lo) = unpack "NN", $header;
- my $resp = ($resp_hi * 2 ** 32) + $resp_lo;
-
- return $resp, $rcas;
-}
-
-sub _incrdecr {
- my $self = shift;
- my ($v, $c) = $self->_incrdecr_cas(@_);
- return $v
-}
-
-sub silent_incrdecr {
- my $self = shift;
- my ($cmd, $key, $amt, $init, $exp) = @_;
- my $opaque = 8275753;
-
- $mc->send_silent($cmd, $key, '', $opaque,
- $mc->_incrdecr_header($amt, $init, $exp));
-}
-
-sub stats {
- my $self = shift;
- my $key = shift;
- my $cas = 0;
- my $opaque = int(rand(2**32));
- $self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas);
-
- my %rv = ();
- my $found_key = '';
- my $found_val = '';
- do {
- my ($op, $data, $cas, $keylen) = $self->_handle_single_response($opaque);
- if($keylen > 0) {
- $found_key = substr($data, 0, $keylen);
- $found_val = substr($data, $keylen);
- $rv{$found_key} = $found_val;
- } else {
- $found_key = '';
- }
- } while($found_key ne '');
- return %rv;
-}
-
-sub get {
- my $self = shift;
- my $key = shift;
- my ($rv, $cas) = $self->_do_command(::CMD_GET, $key, '', '');
-
- my $header = substr $rv, 0, 4, '';
- my $flags = unpack("N", $header);
-
- return ($flags, $rv, $cas);
-}
-
-sub get_multi {
- my $self = shift;
- my @keys = @_;
-
- for (my $i = 0; $i < @keys; $i++) {
- $self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0);
- }
-
- my $terminal = @keys + 10;
- $self->send_command(::CMD_NOOP, '', '', $terminal);
-
- my %return;
- while (1) {
- my ($opaque, $data) = $self->_handle_single_response;
- last if $opaque == $terminal;
-
- my $header = substr $data, 0, 4, '';
- my $flags = unpack("N", $header);
-
- $return{$keys[$opaque]} = [$flags, $data];
- }
-
- return %return if wantarray;
- return \%return;
-}
-
-sub touch {
- my $self = shift;
- my ($key, $expire) = @_;
- my $extra_header = pack "N", $expire;
- my $cas = 0;
- return $self->_do_command(::CMD_TOUCH, $key, '', $extra_header, $cas);
-}
-
-sub gat {
- my $self = shift;
- my $key = shift;
- my $expire = shift;
- my $extra_header = pack "N", $expire;
- my ($rv, $cas) = $self->_do_command(::CMD_GAT, $key, '', $extra_header);
-
- my $header = substr $rv, 0, 4, '';
- my $flags = unpack("N", $header);
-
- return ($flags, $rv, $cas);
-}
-
-sub version {
- my $self = shift;
- return $self->_do_command(::CMD_VERSION, '', '');
-}
-
-sub flush {
- my $self = shift;
- return $self->_do_command(::CMD_FLUSH, '', '');
-}
-
-sub add {
- my $self = shift;
- my ($key, $val, $flags, $expire) = @_;
- my $extra_header = pack "NN", $flags, $expire;
- my $cas = 0;
- return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas);
-}
-
-sub set {
- my $self = shift;
- my ($key, $val, $flags, $expire, $cas) = @_;
- my $extra_header = pack "NN", $flags, $expire;
- return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas);
-}
-
-sub _append_prepend {
- my $self = shift;
- my ($cmd, $key, $val, $cas) = @_;
- return $self->_do_command($cmd, $key, $val, '', $cas);
-}
-
-sub replace {
- my $self = shift;
- my ($key, $val, $flags, $expire) = @_;
- my $extra_header = pack "NN", $flags, $expire;
- my $cas = 0;
- return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas);
-}
-
-sub delete {
- my $self = shift;
- my ($key) = @_;
- return $self->_do_command(::CMD_DELETE, $key, '');
-}
-
-sub incr {
- my $self = shift;
- my ($key, $amt, $init, $exp) = @_;
- $amt = 1 unless defined $amt;
- $init = 0 unless defined $init;
- $exp = 0 unless defined $exp;
-
- return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp);
-}
-
-sub incr_cas {
- my $self = shift;
- my ($key, $amt, $init, $exp) = @_;
- $amt = 1 unless defined $amt;
- $init = 0 unless defined $init;
- $exp = 0 unless defined $exp;
-
- return $self->_incrdecr_cas(::CMD_INCR, $key, $amt, $init, $exp);
-}
-
-sub decr {
- my $self = shift;
- my ($key, $amt, $init, $exp) = @_;
- $amt = 1 unless defined $amt;
- $init = 0 unless defined $init;
- $exp = 0 unless defined $exp;
-
- return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp);
-}
-
-sub noop {
- my $self = shift;
- return $self->_do_command(::CMD_NOOP, '', '');
-}
-
-package MC::Error;
-
-use strict;
-use warnings;
-
-use constant ERR_UNKNOWN_CMD => 0x81;
-use constant ERR_NOT_FOUND => 0x1;
-use constant ERR_EXISTS => 0x2;
-use constant ERR_TOO_BIG => 0x3;
-use constant ERR_EINVAL => 0x4;
-use constant ERR_NOT_STORED => 0x5;
-use constant ERR_DELTA_BADVAL => 0x6;
-
-use overload '""' => sub {
- my $self = shift;
- return "Memcache Error ($self->[0]): $self->[1]";
-};
-
-sub new {
- my $class = shift;
- my $error = [@_];
- my $self = bless $error, (ref $class || $class);
-
- return $self;
-}
-
-sub not_found {
- my $self = shift;
- return $self->[0] == ERR_NOT_FOUND;
-}
-
-sub exists {
- my $self = shift;
- return $self->[0] == ERR_EXISTS;
-}
-
-sub too_big {
- my $self = shift;
- return $self->[0] == ERR_TOO_BIG;
-}
-
-sub delta_badval {
- my $self = shift;
- return $self->[0] == ERR_DELTA_BADVAL;
-}
-
-sub einval {
- my $self = shift;
- return $self->[0] == ERR_EINVAL;
-}
-
-# vim: filetype=perl
-
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 43;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-
-my $server = new_memcached();
-my $sock = $server->sock;
-my $sock2 = $server->new_sock;
-
-my @result;
-my @result2;
-
-ok($sock != $sock2, "have two different connections open");
-
-sub check_args {
- my ($line, $name) = @_;
-
- my $svr = new_memcached();
- my $s = $svr->sock;
-
- print $s $line;
- is(scalar <$s>, "CLIENT_ERROR bad command line format\r\n", $name);
- undef $svr;
-}
-
-check_args "cas bad blah 0 0 0\r\n\r\n", "bad flags";
-check_args "cas bad 0 blah 0 0\r\n\r\n", "bad exp";
-check_args "cas bad 0 0 blah 0\r\n\r\n", "bad cas";
-check_args "cas bad 0 0 0 blah\r\n\r\n", "bad size";
-
-# gets foo (should not exist)
-print $sock "gets foo\r\n";
-is(scalar <$sock>, "END\r\n", "gets failed");
-
-# set foo
-print $sock "set foo 0 0 6\r\nbarval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored barval");
-
-# gets foo and verify identifier exists
-@result = mem_gets($sock, "foo");
-mem_gets_is($sock,$result[0],"foo","barval");
-
-# cas fail
-print $sock "cas foo 0 0 6 123\r\nbarva2\r\n";
-is(scalar <$sock>, "EXISTS\r\n", "cas failed for foo");
-
-# gets foo - success
-@result = mem_gets($sock, "foo");
-mem_gets_is($sock,$result[0],"foo","barval");
-
-# cas success
-print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n";
-is(scalar <$sock>, "STORED\r\n", "cas success, set foo");
-
-# cas failure (reusing the same key)
-print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n";
-is(scalar <$sock>, "EXISTS\r\n", "reusing a CAS ID");
-
-# delete foo
-print $sock "delete foo\r\n";
-is(scalar <$sock>, "DELETED\r\n", "deleted foo");
-
-# cas missing
-print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n";
-is(scalar <$sock>, "NOT_FOUND\r\n", "cas failed, foo does not exist");
-
-# cas empty
-print $sock "cas foo 0 0 6 \r\nbarva2\r\n";
-is(scalar <$sock>, "ERROR\r\n", "cas empty, throw error");
-# cant parse barval2\r\n
-is(scalar <$sock>, "ERROR\r\n", "error out on barval2 parsing");
-
-# set foo1
-print $sock "set foo1 0 0 1\r\n1\r\n";
-is(scalar <$sock>, "STORED\r\n", "set foo1");
-# set foo2
-print $sock "set foo2 0 0 1\r\n2\r\n";
-is(scalar <$sock>, "STORED\r\n", "set foo2");
-
-# gets foo1 check
-print $sock "gets foo1\r\n";
-ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "gets foo1 regexp success");
-my $foo1_cas = $1;
-is(scalar <$sock>, "1\r\n","gets foo1 data is 1");
-is(scalar <$sock>, "END\r\n","gets foo1 END");
-
-# gets foo2 check
-print $sock "gets foo2\r\n";
-ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/,"gets foo2 regexp success");
-my $foo2_cas = $1;
-is(scalar <$sock>, "2\r\n","gets foo2 data is 2");
-is(scalar <$sock>, "END\r\n","gets foo2 END");
-
-# validate foo1 != foo2
-ok($foo1_cas != $foo2_cas,"foo1 != foo2 single-gets success");
-
-# multi-gets
-print $sock "gets foo1 foo2\r\n";
-ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "validating first set of data is foo1");
-$foo1_cas = $1;
-is(scalar <$sock>, "1\r\n", "validating foo1 set of data is 1");
-ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/, "validating second set of data is foo2");
-$foo2_cas = $1;
-is(scalar <$sock>, "2\r\n", "validating foo2 set of data is 2");
-is(scalar <$sock>, "END\r\n","validating foo1,foo2 gets is over - END");
-
-# validate foo1 != foo2
-ok($foo1_cas != $foo2_cas, "foo1 != foo2 multi-gets success");
-
-### simulate race condition with cas
-
-# gets foo1 - success
-@result = mem_gets($sock, "foo1");
-ok($result[0] != "", "sock - gets foo1 is not empty");
-
-# gets foo2 - success
-@result2 = mem_gets($sock2, "foo1");
-ok($result2[0] != "","sock2 - gets foo1 is not empty");
-
-print $sock "cas foo1 0 0 6 $result[0]\r\nbarva2\r\n";
-print $sock2 "cas foo1 0 0 5 $result2[0]\r\napple\r\n";
-
-my $res1 = <$sock>;
-my $res2 = <$sock2>;
-
-ok( ( $res1 eq "STORED\r\n" && $res2 eq "EXISTS\r\n") ||
- ( $res1 eq "EXISTS\r\n" && $res2 eq "STORED\r\n"),
- "cas on same item from two sockets");
-
-### bug 15: http://code.google.com/p/memcached/issues/detail?id=15
-
-# set foo
-print $sock "set bug15 0 0 1\r\n0\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored 0");
-
-# Check out the first gets.
-print $sock "gets bug15\r\n";
-ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success");
-my $bug15_cas = $1;
-is(scalar <$sock>, "0\r\n", "gets bug15 data is 0");
-is(scalar <$sock>, "END\r\n","gets bug15 END");
-
-# Increment
-print $sock "incr bug15 1\r\n";
-is(scalar <$sock>, "1\r\n", "incr worked");
-
-# Validate a changed CAS
-print $sock "gets bug15\r\n";
-ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success");
-my $next_bug15_cas = $1;
-is(scalar <$sock>, "1\r\n", "gets bug15 data is 0");
-is(scalar <$sock>, "END\r\n","gets bug15 END");
-
-ok($bug15_cas != $next_bug15_cas, "CAS changed");
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached('-M -m 1');
-my $sock = $server->sock;
-
-my $value = "B" x 8192;
-my $vallen = length($value);
-
-my $resp = "STORED\r\n";
-my $key = 0;
-
-while($resp eq "STORED\r\n") {
- print $sock "set dash$key 0 0 $vallen\r\n$value\r\n";
- $key++;
- $resp = scalar <$sock>;
-}
-
-my $max_stored = $key - 1;
-
-plan tests => $max_stored + 1;
-
-print $sock "set dash$key 0 0 $vallen\r\n$value\r\n";
-is(scalar <$sock>, "SERVER_ERROR out of memory storing object\r\n",
- "failed to add another one.");
-
-for($key = 0; $key < $max_stored; $key++) {
- mem_get_is $sock, "dash$key", $value, "Failed at dash$key";
-}
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 15;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-my $expire;
-
-sub wait_for_early_second {
- my $have_hires = eval "use Time::HiRes (); 1";
- if ($have_hires) {
- my $tsh = Time::HiRes::time();
- my $ts = int($tsh);
- return if ($tsh - $ts) < 0.5;
- }
-
- my $ts = int(time());
- while (1) {
- my $t = int(time());
- return if $t != $ts;
- select undef, undef, undef, 0.10; # 1/10th of a second sleeps until time changes.
- }
-}
-
-wait_for_early_second();
-
-print $sock "set foo 0 1 6\r\nfooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored foo");
-
-mem_get_is($sock, "foo", "fooval");
-sleep(1.5);
-mem_get_is($sock, "foo", undef);
-
-$expire = time() - 1;
-print $sock "set foo 0 $expire 6\r\nfooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored foo");
-mem_get_is($sock, "foo", undef, "already expired");
-
-$expire = time() + 1;
-print $sock "set foo 0 $expire 6\r\nfoov+1\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored foo");
-mem_get_is($sock, "foo", "foov+1");
-sleep(2.2);
-mem_get_is($sock, "foo", undef, "now expired");
-
-$expire = time() - 20;
-print $sock "set boo 0 $expire 6\r\nbooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored boo");
-mem_get_is($sock, "boo", undef, "now expired");
-
-print $sock "add add 0 2 6\r\naddval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored add");
-mem_get_is($sock, "add", "addval");
-# second add fails
-print $sock "add add 0 2 7\r\naddval2\r\n";
-is(scalar <$sock>, "NOT_STORED\r\n", "add failure");
-sleep(2.3);
-print $sock "add add 0 2 7\r\naddval3\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored add again");
-mem_get_is($sock, "add", "addval3");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 6;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-
-# set foo (and should get it)
-for my $flags (0, 123, 2**16-1) {
- print $sock "set foo $flags 0 6\r\nfooval\r\n";
- is(scalar <$sock>, "STORED\r\n", "stored foo");
- mem_get_is({ sock => $sock,
- flags => $flags }, "foo", "fooval", "got flags $flags back");
-}
+++ /dev/null
-#!/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 $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');
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 539;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-
-my $server = new_memcached();
-my $sock = $server->sock;
-
-
-# set foo (and should get it)
-print $sock "set foo 0 0 6\r\nfooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored foo");
-mem_get_is($sock, "foo", "fooval");
-
-# add bar (and should get it)
-print $sock "add bar 0 0 6\r\nbarval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored barval");
-mem_get_is($sock, "bar", "barval");
-
-# add foo (but shouldn't get new value)
-print $sock "add foo 0 0 5\r\nfoov2\r\n";
-is(scalar <$sock>, "NOT_STORED\r\n", "not stored");
-mem_get_is($sock, "foo", "fooval");
-
-# replace bar (should work)
-print $sock "replace bar 0 0 6\r\nbarva2\r\n";
-is(scalar <$sock>, "STORED\r\n", "replaced barval 2");
-
-# replace notexist (shouldn't work)
-print $sock "replace notexist 0 0 6\r\nbarva2\r\n";
-is(scalar <$sock>, "NOT_STORED\r\n", "didn't replace notexist");
-
-# delete foo.
-print $sock "delete foo\r\n";
-is(scalar <$sock>, "DELETED\r\n", "deleted foo");
-
-# delete foo again. not found this time.
-print $sock "delete foo\r\n";
-is(scalar <$sock>, "NOT_FOUND\r\n", "deleted foo, but not found");
-
-# add moo
-#
-print $sock "add moo 0 0 6\r\nmooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored barval");
-mem_get_is($sock, "moo", "mooval");
-
-# check-and-set (cas) failure case, try to set value with incorrect cas unique val
-print $sock "cas moo 0 0 6 0\r\nMOOVAL\r\n";
-is(scalar <$sock>, "EXISTS\r\n", "check and set with invalid id");
-
-# test "gets", grab unique ID
-print $sock "gets moo\r\n";
-# VALUE moo 0 6 3084947704
-#
-my @retvals = split(/ /, scalar <$sock>);
-my $data = scalar <$sock>; # grab data
-my $dot = scalar <$sock>; # grab dot on line by itself
-is($retvals[0], "VALUE", "get value using 'gets'");
-my $unique_id = $retvals[4];
-# clean off \r\n
-$unique_id =~ s/\r\n$//;
-ok($unique_id =~ /^\d+$/, "unique ID '$unique_id' is an integer");
-# now test that we can store moo with the correct unique id
-print $sock "cas moo 0 0 6 $unique_id\r\nMOOVAL\r\n";
-is(scalar <$sock>, "STORED\r\n");
-mem_get_is($sock, "moo", "MOOVAL");
-
-# pipeling is okay
-print $sock "set foo 0 0 6\r\nfooval\r\ndelete foo\r\nset foo 0 0 6\r\nfooval\r\ndelete foo\r\n";
-is(scalar <$sock>, "STORED\r\n", "pipeline set");
-is(scalar <$sock>, "DELETED\r\n", "pipeline delete");
-is(scalar <$sock>, "STORED\r\n", "pipeline set");
-is(scalar <$sock>, "DELETED\r\n", "pipeline delete");
-
-
-# Test sets up to a large size around 1MB.
-# Everything up to 1MB - 1k should succeed, everything 1MB +1k should fail.
-
-my $len = 1024;
-while ($len < 1024*1028) {
- my $val = "B"x$len;
- if ($len > (1024*1024)) {
- # Ensure causing a memory overflow doesn't leave stale data.
- print $sock "set foo_$len 0 0 3\r\nMOO\r\n";
- is(scalar <$sock>, "STORED\r\n");
- print $sock "set foo_$len 0 0 $len\r\n$val\r\n";
- is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "failed to store size $len");
- mem_get_is($sock, "foo_$len");
- } else {
- print $sock "set foo_$len 0 0 $len\r\n$val\r\n";
- is(scalar <$sock>, "STORED\r\n", "stored size $len");
- }
- $len += 2048;
-}
-
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/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.");
-
+++ /dev/null
-#!/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..")
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 7;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-my $value1 = "A"x66560;
-my $value2 = "B"x66570;
-
-print $sock "set key 0 1 66560\r\n$value1\r\n";
-is (scalar <$sock>, "STORED\r\n", "stored key");
-
-my $stats = mem_stats($sock, "slabs");
-my $requested = $stats->{"31:mem_requested"};
-isnt ($requested, "0", "We should have requested some memory");
-
-sleep(3);
-print $sock "set key 0 0 66570\r\n$value2\r\n";
-is (scalar <$sock>, "STORED\r\n", "stored key");
-
-my $stats = mem_stats($sock, "items");
-my $reclaimed = $stats->{"items:31:reclaimed"};
-is ($reclaimed, "1", "Objects should be reclaimed");
-
-print $sock "delete key\r\n";
-is (scalar <$sock>, "DELETED\r\n", "deleted key");
-
-print $sock "set key 0 0 66560\r\n$value1\r\n";
-is (scalar <$sock>, "STORED\r\n", "stored key");
-
-my $stats = mem_stats($sock, "slabs");
-my $requested2 = $stats->{"31:mem_requested"};
-is ($requested2, $requested, "we've not allocated and freed the same amont");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 5;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-print $sock "set key 0 0 1\r\n1\r\n";
-is (scalar <$sock>, "STORED\r\n", "stored key");
-my $s1 = mem_stats($sock);
-my $r1 = $s1->{"reclaimed"};
-is ($r1, "0", "Objects should not be reclaimed");
-sleep(2);
-print $sock "flush_all\r\n";
-is (scalar <$sock>, "OK\r\n", "Cache flushed");
-print $sock "set key 0 0 1\r\n1\r\n";
-is (scalar <$sock>, "STORED\r\n", "stored key");
-my $s2 = mem_stats($sock);
-my $r2 = $s2->{"reclaimed"};
-is ($r2, "1", "Objects should be reclaimed");
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/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")
+++ /dev/null
-#!/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 <key> [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.");
-
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 11;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-my $value = "B"x10;
-my $key = 0;
-
-for ($key = 0; $key < 10; $key++) {
- print $sock "set key$key 0 0 10\r\n$value\r\n";
- is (scalar <$sock>, "STORED\r\n", "stored key$key");
-}
-
-my $first_stats = mem_stats($sock, "slabs");
-my $req = $first_stats->{"1:mem_requested"};
-ok ($req == "640" || $req == "800", "Check allocated size");
+++ /dev/null
-#!/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.");
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 22;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-use Carp qw(croak);
-
-use Cwd;
-my $builddir = getcwd;
-
-$ENV{'MEMCACHED_PORT_FILENAME'} = "/tmp/ports.$$";
-
-sub read_ports {
- my %rv = ();
- open(my $f, "/tmp/ports.$$") || die("Can't open ports file.");
- while(<$f>) {
- my ($type, $port) = split(/:\s+/);
- $rv{$type} = $port + 0;
- }
- unlink "/tmp/ports.$$";
- return %rv;
-}
-
-sub validate_port {
- my ($name, $got, $expected) = @_;
- # diag "Wanted $expected, got $got";
- if ($expected == -1) {
- ok(!defined($got), "$name expected no port, got $got");
- } elsif ($expected == 0) {
- ok($got != 11211, "$name expected random port (got $got)");
- } else {
- is($got, $expected, "$name");
- }
-}
-
-sub run_server {
- my ($args) = @_;
-
- my $exe = "$builddir/memcached-debug";
- croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe;
-
- my $childpid = fork();
-
- my $root = '';
- $root = "-u root" if ($< == 0);
- my $cmd = "$builddir/timedrun 10 $exe $root $args";
-
- unless($childpid) {
- exec $cmd;
- exit; # NOTREACHED
- }
-
- for (1..20) {
- if (-f "/tmp/ports.$$") {
- return Memcached::Handle->new(pid => $childpid);
- }
- select undef, undef, undef, 0.10;
- }
- croak "Failed to start server.";
-}
-
-sub when {
- my ($name, $params, $expected_tcp, $expected_udp) = @_;
-
- my $server = run_server($params);
- my %ports = read_ports();
-
- validate_port($name, $ports{'TCP INET'}, $expected_tcp);
- validate_port($name, $ports{'UDP INET'}, $expected_udp);
-}
-
-# Disabling the defaults since it conflicts with a running instance.
-# when('no arguments', '', 11211, 11211);
-when('specifying tcp port', '-p 11212', 11212, 11212);
-when('specifying udp port', '-U 11222', 11222, 11222);
-when('specifying tcp ephemeral port', '-p -1', 0, 0);
-when('specifying udp ephemeral port', '-U -1', 0, 0);
-when('tcp port disabled', '-p 0', -1, -1);
-when('udp port disabled', '-U 0', -1, -1);
-when('specifying tcp and udp ports', '-p 11232 -U 11233', 11232, 11233);
-when('specifying tcp and disabling udp', '-p 11242 -U 0', 11242, -1);
-when('specifying udp and disabling tcp', '-p -1 -U 11252', 0, 11252);
-when('specifying tcp and ephemeral udp', '-p 11262 -U -1', 11262, 0);
-when('specifying udp and ephemeral tcp', '-p -1 -U 11272', 0, 11272);
+++ /dev/null
-#!/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");
-}
-
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 7;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-
-my $stats = mem_stats($sock, ' settings');
-
-# Ensure default still works.
-is($stats->{item_size_max}, 1024 * 1024);
-$server->stop();
-
-# Should die.
-eval {
- $server = new_memcached('-I 1000');
-};
-ok($@ && $@ =~ m/^Failed/, "Shouldn't start with < 1k item max");
-
-eval {
- $server = new_memcached('-I 256m');
-};
-ok($@ && $@ =~ m/^Failed/, "Shouldn't start with > 128m item max");
-
-# Minimum.
-$server = new_memcached('-I 1024');
-my $stats = mem_stats($server->sock, ' settings');
-is($stats->{item_size_max}, 1024);
-$server->stop();
-
-# Reasonable but unreasonable.
-$server = new_memcached('-I 1049600');
-my $stats = mem_stats($server->sock, ' settings');
-is($stats->{item_size_max}, 1049600);
-$server->stop();
-
-# Suffix kilobytes.
-$server = new_memcached('-I 512k');
-my $stats = mem_stats($server->sock, ' settings');
-is($stats->{item_size_max}, 524288);
-$server->stop();
-
-# Suffix megabytes.
-$server = new_memcached('-I 32m');
-my $stats = mem_stats($server->sock, ' settings');
-is($stats->{item_size_max}, 33554432);
-$server->stop();
-
+++ /dev/null
-package MemcachedTest;
-use strict;
-use IO::Socket::INET;
-use IO::Socket::UNIX;
-use Exporter 'import';
-use Carp qw(croak);
-use vars qw(@EXPORT);
-
-# Instead of doing the substitution with Autoconf, we assume that
-# cwd == builddir.
-use Cwd;
-my $builddir = getcwd;
-
-
-@EXPORT = qw(new_memcached sleep mem_get_is mem_gets mem_gets_is mem_stats
- supports_sasl free_port);
-
-sub sleep {
- my $n = shift;
- select undef, undef, undef, $n;
-}
-
-sub mem_stats {
- my ($sock, $type) = @_;
- $type = $type ? " $type" : "";
- print $sock "stats$type\r\n";
- my $stats = {};
- while (<$sock>) {
- last if /^(\.|END)/;
- /^(STAT|ITEM) (\S+)\s+([^\r\n]+)/;
- #print " slabs: $_";
- $stats->{$2} = $3;
- }
- return $stats;
-}
-
-sub mem_get_is {
- # works on single-line values only. no newlines in value.
- my ($sock_opts, $key, $val, $msg) = @_;
- my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {};
- my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts;
-
- my $expect_flags = $opts->{flags} || 0;
- my $dval = defined $val ? "'$val'" : "<undef>";
- $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'" : "<undef>";
- $msg ||= "$key == $dval";
-
- print $sock "gets $key\r\n";
- if (! defined $val) {
- my $line = scalar <$sock>;
- if ($line =~ /^VALUE/) {
- $line .= scalar(<$sock>) . scalar(<$sock>);
- }
- Test::More::is($line, "END\r\n", $msg);
- } else {
- my $len = length($val);
- my $body = scalar(<$sock>);
- my $expected = "VALUE $key $expect_flags $len $identifier\r\n$val\r\nEND\r\n";
- if (!$body || $body =~ /^END/) {
- Test::More::is($body, $expected, $msg);
- return;
- }
- $body .= scalar(<$sock>) . scalar(<$sock>);
- Test::More::is($body, $expected, $msg);
- }
-}
-
-sub free_port {
- my $type = shift || "tcp";
- my $sock;
- my $port;
- while (!$sock) {
- $port = int(rand(20000)) + 30000;
- $sock = IO::Socket::INET->new(LocalAddr => '127.0.0.1',
- LocalPort => $port,
- Proto => $type,
- ReuseAddr => 1);
- }
- return $port;
-}
-
-sub supports_udp {
- my $output = `$builddir/memcached-debug -h`;
- return 0 if $output =~ /^memcached 1\.1\./;
- return 1;
-}
-
-sub supports_sasl {
- my $output = `$builddir/memcached-debug -h`;
- return 1 if $output =~ /sasl/i;
- return 0;
-}
-
-sub new_memcached {
- my ($args, $passed_port) = @_;
- my $port = $passed_port || free_port();
- my $host = '127.0.0.1';
-
- if ($ENV{T_MEMD_USE_DAEMON}) {
- my ($host, $port) = ($ENV{T_MEMD_USE_DAEMON} =~ m/^([^:]+):(\d+)$/);
- my $conn = IO::Socket::INET->new(PeerAddr => "$host:$port");
- if ($conn) {
- return Memcached::Handle->new(conn => $conn,
- host => $host,
- port => $port);
- }
- croak("Failed to connect to specified memcached server.") unless $conn;
- }
-
- my $udpport = free_port("udp");
- $args .= " -p $port";
- if (supports_udp()) {
- $args .= " -U $udpport";
- }
- if ($< == 0) {
- $args .= " -u root";
- }
-
- my $childpid = fork();
-
- my $exe = "$builddir/memcached-debug";
- croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe;
- croak("memcached binary not executable\n") unless -x _;
-
- unless ($childpid) {
- exec "$builddir/timedrun 600 $exe $args";
- exit; # never gets here.
- }
-
- # unix domain sockets
- if ($args =~ /-s (\S+)/) {
- sleep 1;
- my $filename = $1;
- my $conn = IO::Socket::UNIX->new(Peer => $filename) ||
- croak("Failed to connect to unix domain socket: $! '$filename'");
-
- return Memcached::Handle->new(pid => $childpid,
- conn => $conn,
- domainsocket => $filename,
- host => $host,
- port => $port);
- }
-
- # try to connect / find open port, only if we're not using unix domain
- # sockets
-
- for (1..20) {
- my $conn = IO::Socket::INET->new(PeerAddr => "127.0.0.1:$port");
- if ($conn) {
- return Memcached::Handle->new(pid => $childpid,
- conn => $conn,
- udpport => $udpport,
- host => $host,
- port => $port);
- }
- select undef, undef, undef, 0.10;
- }
- croak("Failed to startup/connect to memcached server.");
-}
-
-############################################################################
-package Memcached::Handle;
-sub new {
- my ($class, %params) = @_;
- return bless \%params, $class;
-}
-
-sub DESTROY {
- my $self = shift;
- kill 2, $self->{pid};
-}
-
-sub stop {
- my $self = shift;
- kill 15, $self->{pid};
-}
-
-sub host { $_[0]{host} }
-sub port { $_[0]{port} }
-sub udpport { $_[0]{udpport} }
-
-sub sock {
- my $self = shift;
-
- if ($self->{conn} && ($self->{domainsocket} || getpeername($self->{conn}))) {
- return $self->{conn};
- }
- return $self->new_sock;
-}
-
-sub new_sock {
- my $self = shift;
- if ($self->{domainsocket}) {
- return IO::Socket::UNIX->new(Peer => $self->{domainsocket});
- } else {
- return IO::Socket::INET->new(PeerAddr => "$self->{host}:$self->{port}");
- }
-}
-
-sub new_udp_sock {
- my $self = shift;
- return IO::Socket::INET->new(PeerAddr => '127.0.0.1',
- PeerPort => $self->{udpport},
- Proto => 'udp',
- LocalAddr => '127.0.0.1',
- LocalPort => MemcachedTest::free_port('udp'),
- );
-
-}
-
-1;
+++ /dev/null
-#!/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");
-}
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 149;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-# assuming max slab is 1M and default mem is 64M
-my $server = new_memcached();
-my $sock = $server->sock;
-
-# create a big value for the largest slab
-my $max = 1024 * 1024;
-my $big = 'x' x (1024 * 1024 - 250);
-
-ok(length($big) > 512 * 1024);
-ok(length($big) < 1024 * 1024);
-
-# test that an even bigger value is rejected while we're here
-my $too_big = $big . $big . $big;
-my $len = length($too_big);
-print $sock "set too_big 0 0 $len\r\n$too_big\r\n";
-is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "too_big not stored");
-
-# set the big value
-my $len = length($big);
-print $sock "set big 0 0 $len\r\n$big\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored big");
-mem_get_is($sock, "big", $big);
-
-# no evictions yet
-my $stats = mem_stats($sock);
-is($stats->{"evictions"}, "0", "no evictions to start");
-
-# set many big items, enough to get evictions
-for (my $i = 0; $i < 100; $i++) {
- print $sock "set item_$i 0 0 $len\r\n$big\r\n";
- is(scalar <$sock>, "STORED\r\n", "stored item_$i");
-}
-
-# some evictions should have happened
-my $stats = mem_stats($sock);
-my $evictions = int($stats->{"evictions"});
-ok($evictions == 37, "some evictions happened");
-
-# the first big value should be gone
-mem_get_is($sock, "big", undef);
-
-# the earliest items should be gone too
-for (my $i = 0; $i < $evictions - 1; $i++) {
- mem_get_is($sock, "item_$i", undef);
-}
-
-# check that the non-evicted are the right ones
-for (my $i = $evictions - 1; $i < $evictions + 4; $i++) {
- mem_get_is($sock, "item_$i", $big);
-}
+++ /dev/null
-#!/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);
-}
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/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");
-
+++ /dev/null
-mech_list: plain cram-md5
-log_level: 5
-sasldb_path: /tmp/test-memcached.sasldb
+++ /dev/null
-#!/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.
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 95;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-my $server = new_memcached();
-my $sock = $server->sock;
-
-
-## Output looks like this:
-##
-## STAT pid 22969
-## STAT uptime 13
-## STAT time 1259170891
-## STAT version 1.4.3
-## STAT libevent 1.4.13-stable.
-## STAT pointer_size 32
-## STAT rusage_user 0.001198
-## STAT rusage_system 0.003523
-## STAT curr_connections 10
-## STAT total_connections 11
-## STAT connection_structures 11
-## STAT cmd_get 0
-## STAT cmd_set 0
-## STAT cmd_flush 0
-## STAT get_hits 0
-## STAT get_misses 0
-## STAT delete_misses 0
-## STAT delete_hits 0
-## STAT incr_misses 0
-## STAT incr_hits 0
-## STAT decr_misses 0
-## STAT decr_hits 0
-## STAT cas_misses 0
-## STAT cas_hits 0
-## STAT cas_badval 0
-## STAT auth_cmds 0
-## STAT auth_unknowns 0
-## STAT bytes_read 7
-## STAT bytes_written 0
-## STAT limit_maxbytes 67108864
-## STAT accepting_conns 1
-## STAT listen_disabled_num 0
-## STAT threads 4
-## STAT conn_yields 0
-## STAT bytes 0
-## STAT curr_items 0
-## STAT total_items 0
-## STAT evictions 0
-## STAT reclaimed 0
-
-# note that auth stats are tested in auth specfic tests
-
-
-my $stats = mem_stats($sock);
-
-# Test number of keys
-is(scalar(keys(%$stats)), 48, "48 stats values");
-
-# Test initial state
-foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses
- bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits
- decr_misses listen_disabled_num)) {
- is($stats->{$key}, 0, "initial $key is zero");
-}
-is($stats->{accepting_conns}, 1, "initial accepting_conns is one");
-
-# Do some operations
-
-print $sock "set foo 0 0 6\r\nfooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored foo");
-mem_get_is($sock, "foo", "fooval");
-
-my $stats = mem_stats($sock);
-
-foreach my $key (qw(total_items curr_items cmd_get cmd_set get_hits)) {
- is($stats->{$key}, 1, "after one set/one get $key is 1");
-}
-
-my $cache_dump = mem_stats($sock, " cachedump 1 100");
-ok(defined $cache_dump->{'foo'}, "got foo from cachedump");
-
-print $sock "delete foo\r\n";
-is(scalar <$sock>, "DELETED\r\n", "deleted foo");
-
-my $stats = mem_stats($sock);
-is($stats->{delete_hits}, 1);
-is($stats->{delete_misses}, 0);
-
-print $sock "delete foo\r\n";
-is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't delete foo again");
-
-my $stats = mem_stats($sock);
-is($stats->{delete_hits}, 1);
-is($stats->{delete_misses}, 1);
-
-# incr stats
-
-sub check_incr_stats {
- my ($ih, $im, $dh, $dm) = @_;
- my $stats = mem_stats($sock);
-
- is($stats->{incr_hits}, $ih);
- is($stats->{incr_misses}, $im);
- is($stats->{decr_hits}, $dh);
- is($stats->{decr_misses}, $dm);
-}
-
-print $sock "incr i 1\r\n";
-is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't incr a missing thing");
-check_incr_stats(0, 1, 0, 0);
-
-print $sock "decr d 1\r\n";
-is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't decr a missing thing");
-check_incr_stats(0, 1, 0, 1);
-
-print $sock "set n 0 0 1\r\n0\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored n");
-
-print $sock "incr n 3\r\n";
-is(scalar <$sock>, "3\r\n", "incr works");
-check_incr_stats(1, 1, 0, 1);
-
-print $sock "decr n 1\r\n";
-is(scalar <$sock>, "2\r\n", "decr works");
-check_incr_stats(1, 1, 1, 1);
-
-# cas stats
-
-sub check_cas_stats {
- my ($ch, $cm, $cb) = @_;
- my $stats = mem_stats($sock);
-
- is($stats->{cas_hits}, $ch);
- is($stats->{cas_misses}, $cm);
- is($stats->{cas_badval}, $cb);
-}
-
-check_cas_stats(0, 0, 0);
-
-print $sock "cas c 0 0 1 99999999\r\nz\r\n";
-is(scalar <$sock>, "NOT_FOUND\r\n", "missed cas");
-check_cas_stats(0, 1, 0);
-
-print $sock "set c 0 0 1\r\nx\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored c");
-my ($id, $v) = mem_gets($sock, 'c');
-is('x', $v, 'got the expected value');
-
-print $sock "cas c 0 0 1 99999999\r\nz\r\n";
-is(scalar <$sock>, "EXISTS\r\n", "missed cas");
-check_cas_stats(0, 1, 1);
-my ($newid, $v) = mem_gets($sock, 'c');
-is('x', $v, 'got the expected value');
-
-print $sock "cas c 0 0 1 $id\r\nz\r\n";
-is(scalar <$sock>, "STORED\r\n", "good cas");
-check_cas_stats(1, 1, 1);
-my ($newid, $v) = mem_gets($sock, 'c');
-is('z', $v, 'got the expected value');
-
-my $settings = mem_stats($sock, ' settings');
-is(1024, $settings->{'maxconns'});
-is('NULL', $settings->{'domain_socket'});
-is('on', $settings->{'evictions'});
-is('yes', $settings->{'cas_enabled'});
-is('no', $settings->{'auth_enabled_sasl'});
-
-print $sock "stats reset\r\n";
-is(scalar <$sock>, "RESET\r\n", "good stats reset");
-
-my $stats = mem_stats($sock);
-is(0, $stats->{'cmd_get'});
-is(0, $stats->{'cmd_set'});
-is(0, $stats->{'get_hits'});
-is(0, $stats->{'get_misses'});
-is(0, $stats->{'delete_misses'});
-is(0, $stats->{'delete_hits'});
-is(0, $stats->{'incr_misses'});
-is(0, $stats->{'incr_hits'});
-is(0, $stats->{'decr_misses'});
-is(0, $stats->{'decr_hits'});
-is(0, $stats->{'cas_misses'});
-is(0, $stats->{'cas_hits'});
-is(0, $stats->{'cas_badval'});
-is(0, $stats->{'evictions'});
-is(0, $stats->{'reclaimed'});
-
-print $sock "flush_all\r\n";
-is(scalar <$sock>, "OK\r\n", "flushed");
-
-my $stats = mem_stats($sock);
-is($stats->{cmd_flush}, 1, "after one flush cmd_flush is 1");
+++ /dev/null
-#!/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; }
- $_;
-}
+++ /dev/null
-#!/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");
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Test::More tests => 48;
-use FindBin qw($Bin);
-use lib "$Bin/lib";
-use MemcachedTest;
-
-use constant IS_ASCII => 0;
-use constant IS_BINARY => 1;
-use constant ENTRY_EXISTS => 0;
-use constant ENTRY_MISSING => 1;
-use constant BIN_REQ_MAGIC => 0x80;
-use constant BIN_RES_MAGIC => 0x81;
-use constant CMD_GET => 0x00;
-use constant CMD_SET => 0x01;
-use constant CMD_ADD => 0x02;
-use constant CMD_REPLACE => 0x03;
-use constant CMD_DELETE => 0x04;
-use constant CMD_INCR => 0x05;
-use constant CMD_DECR => 0x06;
-use constant CMD_APPEND => 0x0E;
-use constant CMD_PREPEND => 0x0F;
-use constant REQ_PKT_FMT => "CCnCCnNNNN";
-use constant RES_PKT_FMT => "CCnCCnNNNN";
-use constant INCRDECR_PKT_FMT => "NNNNN";
-use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT));
-
-
-my $server = new_memcached();
-my $sock = $server->sock;
-
-# set foo (and should get it)
-print $sock "set foo 0 0 6\r\nfooval\r\n";
-is(scalar <$sock>, "STORED\r\n", "stored foo");
-mem_get_is($sock, "foo", "fooval");
-
-my $usock = $server->new_udp_sock
- or die "Can't bind : $@\n";
-
-# testing sequence of request ids
-for my $offt (1, 1, 2) {
- my $req = 160 + $offt;
- my $res = send_udp_request($usock, $req, "get foo\r\n");
- ok($res, "got result");
- is(keys %$res, 1, "one key (one packet)");
- ok($res->{0}, "only got seq number 0");
- is(substr($res->{0}, 8), "VALUE foo 0 6\r\nfooval\r\nEND\r\n");
- is(hexify(substr($res->{0}, 0, 2)), hexify(pack("n", $req)), "udp request number in response ($req) is correct");
-}
-
-# op tests
-for my $prot (::IS_ASCII,::IS_BINARY) {
- udp_set_test($prot,45,"aval$prot","1",0,0);
- udp_set_test($prot,45,"bval$prot","abcd" x 1024,0,0);
- udp_get_test($prot,45,"aval$prot","1",::ENTRY_EXISTS);
- udp_get_test($prot,45,"404$prot","1",::ENTRY_MISSING);
- udp_incr_decr_test($prot,45,"aval$prot","1","incr",1);
- udp_incr_decr_test($prot,45,"aval$prot","1","decr",2);
- udp_delete_test($prot,45,"aval$prot");
-}
-
-sub udp_set_test {
- my ($protocol, $req_id, $key, $value, $flags, $exp) = @_;
- my $req = "";
- my $val_len = length($value);
-
- if ($protocol == ::IS_ASCII) {
- $req = "set $key $flags $exp $val_len\r\n$value\r\n";
- } elsif ($protocol == ::IS_BINARY) {
- my $key_len = length($key);
- my $extra = pack "NN",$flags,$exp;
- my $extra_len = length($extra);
- my $total_len = $val_len + $extra_len + $key_len;
- $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_SET, $key_len, $extra_len, 0, 0, $total_len, 0, 0, 0);
- $req .= $extra . $key . $value;
- }
-
- my $datagrams = send_udp_request($usock, $req_id, $req);
- my $resp = construct_udp_message($datagrams);
-
- if ($protocol == ::IS_ASCII) {
- is($resp,"STORED\r\n","Store key $key using ASCII protocol");
- } elsif ($protocol == ::IS_BINARY) {
- my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
- $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
- is($resp_status,"0","Store key $key using binary protocol");
- }
-}
-
-sub udp_get_test {
- my ($protocol, $req_id, $key, $value, $exists) = @_;
- my $key_len = length($key);
- my $value_len = length($value);
- my $req = "";
-
- if ($protocol == ::IS_ASCII) {
- $req = "get $key\r\n";
- } elsif ($protocol == ::IS_BINARY) {
- $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_GET, $key_len, 0, 0, 0, $key_len, 0, 0, 0);
- $req .= $key;
- }
-
- my $datagrams = send_udp_request($usock, $req_id, $req);
- my $resp = construct_udp_message($datagrams);
-
- if ($protocol == ::IS_ASCII) {
- if ($exists == ::ENTRY_EXISTS) {
- is($resp,"VALUE $key 0 $value_len\r\n$value\r\nEND\r\n","Retrieve entry with key $key using ASCII protocol");
- } else {
- is($resp,"END\r\n","Retrieve non existing entry with key $key using ASCII protocol");
- }
- } elsif ($protocol == ::IS_BINARY) {
- my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
- $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
- if ($exists == ::ENTRY_EXISTS) {
- is($resp_status,"0","Retrieve entry with key $key using binary protocol");
- is(substr($resp,::MIN_RECV_BYTES + $resp_extra_len + $resp_key_len, $value_len),$value,"Value for key $key retrieved with binary protocol matches");
- } else {
- is($resp_status,"1","Retrieve non existing entry with key $key using binary protocol");
- }
- }
-}
-
-sub udp_delete_test {
- my ($protocol, $req_id, $key) = @_;
- my $req = "";
- my $key_len = length($key);
-
- if ($protocol == ::IS_ASCII) {
- $req = "delete $key\r\n";
- } elsif ($protocol == ::IS_BINARY) {
- $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_DELETE, $key_len, 0, 0, 0, $key_len, 0, 0, 0);
- $req .= $key;
- }
-
- my $datagrams = send_udp_request($usock, $req_id, $req);
- my $resp = construct_udp_message($datagrams);
-
- if ($protocol == ::IS_ASCII) {
- is($resp,"DELETED\r\n","Delete key $key using ASCII protocol");
- } elsif ($protocol == ::IS_BINARY) {
- my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
- $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
- is($resp_status,"0","Delete key $key using binary protocol");
- }
-}
-
-sub udp_incr_decr_test {
- my ($protocol, $req_id, $key, $val, $optype, $init_val) = @_;
- my $req = "";
- my $key_len = length($key);
- my $expected_value = 0;
- my $acmd = "incr";
- my $bcmd = ::CMD_INCR;
- if ($optype eq "incr") {
- $expected_value = $init_val + $val;
- } else {
- $acmd = "decr";
- $bcmd = ::CMD_DECR;
- $expected_value = $init_val - $val;
- }
-
- if ($protocol == ::IS_ASCII) {
- $req = "$acmd $key $val\r\n";
- } elsif ($protocol == ::IS_BINARY) {
- my $extra = pack(::INCRDECR_PKT_FMT, ($val / 2 ** 32),($val % 2 ** 32), 0, 0, 0);
- my $extra_len = length($extra);
- $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, $bcmd, $key_len, $extra_len, 0, 0, $key_len + $extra_len, 0, 0, 0);
- $req .= $extra . $key;
- }
-
- my $datagrams = send_udp_request($usock, $req_id, $req);
- my $resp = construct_udp_message($datagrams);
-
- if ($protocol == ::IS_ASCII) {
- is($resp,"$expected_value\r\n","perform $acmd math operation on key $key with ASCII protocol");
- } elsif ($protocol == ::IS_BINARY) {
- my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
- $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
- is($resp_status,"0","perform $acmd math operation on key $key with binary protocol");
- my ($resp_hi,$resp_lo) = unpack("NN",substr($resp,::MIN_RECV_BYTES + $resp_extra_len + $resp_key_len,
- $resp_total_len - $resp_extra_len - $resp_key_len));
- is(($resp_hi * 2 ** 32) + $resp_lo,$expected_value,"validate result of binary protocol math operation $acmd . Expected value $expected_value")
- }
-}
-
-sub construct_udp_message {
- my $datagrams = shift;
- my $num_datagram = keys (%$datagrams);
- my $msg = "";
- my $cur_dg ="";
- my $cur_udp_header ="";
- for (my $cur_dg_index = 0; $cur_dg_index < $num_datagram; $cur_dg_index++) {
- $cur_dg = $datagrams->{$cur_dg_index};
- isnt($cur_dg,"","missing datagram for segment $cur_dg_index");
- $cur_udp_header=substr($cur_dg, 0, 8);
- $msg .= substr($cur_dg,8);
- }
- return $msg;
-}
-
-sub hexify {
- my $val = shift;
- $val =~ s/(.)/sprintf("%02x", ord($1))/egs;
- return $val;
-}
-
-# returns undef on select timeout, or hashref of "seqnum" -> payload (including headers)
-# verifies that resp_id is equal to id sent in request
-# ensures consistency in num packets that make up response
-sub send_udp_request {
- my ($sock, $reqid, $req) = @_;
-
- my $pkt = pack("nnnn", $reqid, 0, 1, 0); # request id (opaque), seq num, #packets, reserved (must be 0)
- $pkt .= $req;
- my $fail = sub {
- my $msg = shift;
- warn " FAILING send_udp because: $msg\n";
- return undef;
- };
- return $fail->("send") unless send($sock, $pkt, 0);
-
- my $ret = {};
-
- my $got = 0; # packets got
- my $numpkts = undef;
-
- while (!defined($numpkts) || $got < $numpkts) {
- my $rin = '';
- vec($rin, fileno($sock), 1) = 1;
- my $rout;
- return $fail->("timeout after $got packets") unless
- select($rout = $rin, undef, undef, 1.5);
-
- my $res;
- my $sender = $sock->recv($res, 1500, 0);
- my ($resid, $seq, $this_numpkts, $resv) = unpack("nnnn", substr($res, 0, 8));
- die "Response ID of $resid doesn't match request if of $reqid" unless $resid == $reqid;
- die "Reserved area not zero" unless $resv == 0;
- die "num packets changed midstream!" if defined $numpkts && $this_numpkts != $numpkts;
- $numpkts = $this_numpkts;
- $ret->{$seq} = $res;
- $got++;
- }
- return $ret;
-}
-
-
-__END__
- $sender = recv($usock, $ans, 1050, 0);
-
-__END__
- $usock->send
-
-
- ($hispaddr = recv(SOCKET, $rtime, 4, 0)) || die "recv: $!";
-($port, $hisiaddr) = sockaddr_in($hispaddr);
-$host = gethostbyaddr($hisiaddr, AF_INET);
-$histime = unpack("N", $rtime) - $SECS_of_70_YEARS ;
+++ /dev/null
-#!/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...
+++ /dev/null
-#!/usr/bin/perl
-use strict;
-use FindBin qw($Bin);
-our @files;
-
-BEGIN {
- chdir "$Bin/.." or die;
-
- my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md);
- push(@exempted, glob("doc/*.xml"));
- push(@exempted, glob("doc/xml2rfc/*.xsl"));
- push(@exempted, glob("m4/*backport*m4"));
- push(@exempted, glob("*.orig"));
- 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)");
-}
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-#undef NDEBUG
-#include <pthread.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <fcntl.h>
-
-#include "config.h"
-#include "cache.h"
-#include "util.h"
-#include "protocol_binary.h"
-
-#define TMP_TEMPLATE "/tmp/test_file.XXXXXXX"
-
-enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
-
-static pid_t server_pid;
-static in_port_t port;
-static int sock;
-static bool allow_closed_read = false;
-
-static enum test_return cache_create_test(void)
-{
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, NULL);
- assert(cache != NULL);
- cache_destroy(cache);
- return TEST_PASS;
-}
-
-const uint64_t constructor_pattern = 0xdeadcafebabebeef;
-
-static int cache_constructor(void *buffer, void *notused1, int notused2) {
- uint64_t *ptr = buffer;
- *ptr = constructor_pattern;
- return 0;
-}
-
-static enum test_return cache_constructor_test(void)
-{
- cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
- cache_constructor, NULL);
- assert(cache != NULL);
- uint64_t *ptr = cache_alloc(cache);
- uint64_t pattern = *ptr;
- cache_free(cache, ptr);
- cache_destroy(cache);
- return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
-}
-
-static int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
- return 1;
-}
-
-static enum test_return cache_fail_constructor_test(void)
-{
- enum test_return ret = TEST_PASS;
-
- cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
- cache_fail_constructor, NULL);
- assert(cache != NULL);
- uint64_t *ptr = cache_alloc(cache);
- if (ptr != NULL) {
- ret = TEST_FAIL;
- }
- cache_destroy(cache);
- return ret;
-}
-
-static void *destruct_data = 0;
-
-static void cache_destructor(void *buffer, void *notused) {
- destruct_data = buffer;
-}
-
-static enum test_return cache_destructor_test(void)
-{
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, cache_destructor);
- assert(cache != NULL);
- char *ptr = cache_alloc(cache);
- cache_free(cache, ptr);
- cache_destroy(cache);
-
- return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
-}
-
-static enum test_return cache_reuse_test(void)
-{
- int ii;
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, NULL);
- char *ptr = cache_alloc(cache);
- cache_free(cache, ptr);
- for (ii = 0; ii < 100; ++ii) {
- char *p = cache_alloc(cache);
- assert(p == ptr);
- cache_free(cache, ptr);
- }
- cache_destroy(cache);
- return TEST_PASS;
-}
-
-
-static enum test_return cache_bulkalloc(size_t datasize)
-{
- cache_t *cache = cache_create("test", datasize, sizeof(char*),
- NULL, NULL);
-#define ITERATIONS 1024
- void *ptr[ITERATIONS];
-
- for (int ii = 0; ii < ITERATIONS; ++ii) {
- ptr[ii] = cache_alloc(cache);
- assert(ptr[ii] != 0);
- memset(ptr[ii], 0xff, datasize);
- }
-
- for (int ii = 0; ii < ITERATIONS; ++ii) {
- cache_free(cache, ptr[ii]);
- }
-
-#undef ITERATIONS
- cache_destroy(cache);
- return TEST_PASS;
-}
-
-static enum test_return test_issue_161(void)
-{
- enum test_return ret = cache_bulkalloc(1);
- if (ret == TEST_PASS) {
- ret = cache_bulkalloc(512);
- }
-
- return ret;
-}
-
-static enum test_return cache_redzone_test(void)
-{
-#ifndef HAVE_UMEM_H
- cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
- NULL, NULL);
-
- /* Ignore SIGABORT */
- struct sigaction old_action;
- struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0};
- sigemptyset(&action.sa_mask);
- sigaction(SIGABRT, &action, &old_action);
-
- /* check memory debug.. */
- char *p = cache_alloc(cache);
- char old = *(p - 1);
- *(p - 1) = 0;
- cache_free(cache, p);
- assert(cache_error == -1);
- *(p - 1) = old;
-
- p[sizeof(uint32_t)] = 0;
- cache_free(cache, p);
- assert(cache_error == 1);
-
- /* restore signal handler */
- sigaction(SIGABRT, &old_action, NULL);
-
- cache_destroy(cache);
-
- return TEST_PASS;
-#else
- return TEST_SKIP;
-#endif
-}
-
-static enum test_return test_safe_strtoul(void) {
- uint32_t val;
- assert(safe_strtoul("123", &val));
- assert(val == 123);
- assert(safe_strtoul("+123", &val));
- assert(val == 123);
- assert(!safe_strtoul("", &val)); // empty
- assert(!safe_strtoul("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoul(" issue221", &val)); // non-numeric
- /* Not sure what it does, but this works with ICC :/
- assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
- */
-
- // extremes:
- assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
- assert(val == 4294967295L);
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtoul("4294967296", &val)); // 2**32
- */
- assert(!safe_strtoul("-1", &val)); // negative
- return TEST_PASS;
-}
-
-
-static enum test_return test_safe_strtoull(void) {
- uint64_t val;
- assert(safe_strtoull("123", &val));
- assert(val == 123);
- assert(safe_strtoull("+123", &val));
- assert(val == 123);
- assert(!safe_strtoull("", &val)); // empty
- assert(!safe_strtoull("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
- assert(!safe_strtoull(" issue221", &val)); // non-numeric
-
- // extremes:
- assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
- assert(val == 18446744073709551615ULL);
- assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
- assert(!safe_strtoull("-1", &val)); // negative
- return TEST_PASS;
-}
-
-static enum test_return test_safe_strtoll(void) {
- int64_t val;
- assert(safe_strtoll("123", &val));
- assert(val == 123);
- assert(safe_strtoll("+123", &val));
- assert(val == 123);
- assert(safe_strtoll("-123", &val));
- assert(val == -123);
- assert(!safe_strtoll("", &val)); // empty
- assert(!safe_strtoll("123BOGUS", &val)); // non-numeric
- assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
- assert(!safe_strtoll(" issue221", &val)); // non-numeric
-
- // extremes:
- assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
- assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
- assert(val == 9223372036854775807LL);
- /*
- assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
- assert(val == -9223372036854775808LL);
- */
- assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
-
- // We'll allow space to terminate the string. And leading space.
- assert(safe_strtoll(" 123 foo", &val));
- assert(val == 123);
- return TEST_PASS;
-}
-
-static enum test_return test_safe_strtol(void) {
- int32_t val;
- assert(safe_strtol("123", &val));
- assert(val == 123);
- assert(safe_strtol("+123", &val));
- assert(val == 123);
- assert(safe_strtol("-123", &val));
- assert(val == -123);
- assert(!safe_strtol("", &val)); // empty
- assert(!safe_strtol("123BOGUS", &val)); // non-numeric
- assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
- assert(!safe_strtol(" issue221", &val)); // non-numeric
-
- // extremes:
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
- */
- assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
- assert(val == 2147483647L);
- /* This actually works on 64-bit ubuntu
- assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
- */
-
- // We'll allow space to terminate the string. And leading space.
- assert(safe_strtol(" 123 foo", &val));
- assert(val == 123);
- return TEST_PASS;
-}
-
-/**
- * Function to start the server and let it listen on a random port
- *
- * @param port_out where to store the TCP port number the server is
- * listening on
- * @param daemon set to true if you want to run the memcached server
- * as a daemon process
- * @return the pid of the memcached server
- */
-static pid_t start_server(in_port_t *port_out, bool daemon, int timeout) {
- char environment[80];
- snprintf(environment, sizeof(environment),
- "MEMCACHED_PORT_FILENAME=/tmp/ports.%lu", (long)getpid());
- char *filename= environment + strlen("MEMCACHED_PORT_FILENAME=");
- char pid_file[80];
- snprintf(pid_file, sizeof(pid_file), "/tmp/pid.%lu", (long)getpid());
-
- remove(filename);
- remove(pid_file);
-
-#ifdef __sun
- /* I want to name the corefiles differently so that they don't
- overwrite each other
- */
- char coreadm[128];
- snprintf(coreadm, sizeof(coreadm),
- "coreadm -p core.%%f.%%p %lu", (unsigned long)getpid());
- system(coreadm);
-#endif
-
- pid_t pid = fork();
- assert(pid != -1);
-
- if (pid == 0) {
- /* Child */
- char *argv[20];
- int arg = 0;
- char tmo[24];
- snprintf(tmo, sizeof(tmo), "%u", timeout);
-
- putenv(environment);
-#ifdef __sun
- putenv("LD_PRELOAD=watchmalloc.so.1");
- putenv("MALLOC_DEBUG=WATCH");
-#endif
-
- if (!daemon) {
- argv[arg++] = "./timedrun";
- argv[arg++] = tmo;
- }
- argv[arg++] = "./memcached-debug";
- argv[arg++] = "-p";
- argv[arg++] = "-1";
- argv[arg++] = "-U";
- argv[arg++] = "0";
- /* Handle rpmbuild and the like doing this as root */
- if (getuid() == 0) {
- argv[arg++] = "-u";
- argv[arg++] = "root";
- }
- if (daemon) {
- argv[arg++] = "-d";
- argv[arg++] = "-P";
- argv[arg++] = pid_file;
- }
-#ifdef MESSAGE_DEBUG
- argv[arg++] = "-vvv";
-#endif
- argv[arg++] = NULL;
- assert(execv(argv[0], argv) != -1);
- }
-
- /* Yeah just let us "busy-wait" for the file to be created ;-) */
- while (access(filename, F_OK) == -1) {
- usleep(10);
- }
-
- FILE *fp = fopen(filename, "r");
- if (fp == NULL) {
- fprintf(stderr, "Failed to open the file containing port numbers: %s\n",
- strerror(errno));
- assert(false);
- }
-
- *port_out = (in_port_t)-1;
- char buffer[80];
- while ((fgets(buffer, sizeof(buffer), fp)) != NULL) {
- if (strncmp(buffer, "TCP INET: ", 10) == 0) {
- int32_t val;
- assert(safe_strtol(buffer + 10, &val));
- *port_out = (in_port_t)val;
- }
- }
- fclose(fp);
- assert(remove(filename) == 0);
-
- if (daemon) {
- /* loop and wait for the pid file.. There is a potential race
- * condition that the server just created the file but isn't
- * finished writing the content, so we loop a few times
- * reading as well */
- while (access(pid_file, F_OK) == -1) {
- usleep(10);
- }
-
- fp = fopen(pid_file, "r");
- if (fp == NULL) {
- fprintf(stderr, "Failed to open pid file: %s\n",
- strerror(errno));
- assert(false);
- }
-
- /* Avoid race by retrying 20 times */
- for (int x = 0; x < 20 && fgets(buffer, sizeof(buffer), fp) == NULL; x++) {
- usleep(10);
- }
- fclose(fp);
-
- int32_t val;
- assert(safe_strtol(buffer, &val));
- pid = (pid_t)val;
- }
-
- return pid;
-}
-
-static enum test_return test_issue_44(void) {
- in_port_t port;
- pid_t pid = start_server(&port, true, 15);
- assert(kill(pid, SIGHUP) == 0);
- sleep(1);
- assert(kill(pid, SIGTERM) == 0);
-
- return TEST_PASS;
-}
-
-static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
-{
- struct addrinfo *ai = 0;
- struct addrinfo hints = { .ai_family = AF_UNSPEC,
- .ai_protocol = IPPROTO_TCP,
- .ai_socktype = SOCK_STREAM };
- char service[NI_MAXSERV];
- int error;
-
- (void)snprintf(service, NI_MAXSERV, "%d", port);
- if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
- if (error != EAI_SYSTEM) {
- fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
- } else {
- perror("getaddrinfo()");
- }
- }
-
- return ai;
-}
-
-static int connect_server(const char *hostname, in_port_t port, bool nonblock)
-{
- struct addrinfo *ai = lookuphost(hostname, port);
- int sock = -1;
- if (ai != NULL) {
- if ((sock = socket(ai->ai_family, ai->ai_socktype,
- ai->ai_protocol)) != -1) {
- if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
- fprintf(stderr, "Failed to connect socket: %s\n",
- strerror(errno));
- close(sock);
- sock = -1;
- } else if (nonblock) {
- int flags = fcntl(sock, F_GETFL, 0);
- if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
- fprintf(stderr, "Failed to enable nonblocking mode: %s\n",
- strerror(errno));
- close(sock);
- sock = -1;
- }
- }
- } else {
- fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
- }
-
- freeaddrinfo(ai);
- }
- return sock;
-}
-
-static enum test_return test_vperror(void) {
- int rv = 0;
- int oldstderr = dup(STDERR_FILENO);
- char tmpl[sizeof(TMP_TEMPLATE)+1];
- strncpy(tmpl, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
-
- int newfile = mkstemp(tmpl);
- assert(newfile > 0);
- rv = dup2(newfile, STDERR_FILENO);
- assert(rv == STDERR_FILENO);
- rv = close(newfile);
- assert(rv == 0);
-
- errno = EIO;
- vperror("Old McDonald had a farm. %s", "EI EIO");
-
- /* Restore stderr */
- rv = dup2(oldstderr, STDERR_FILENO);
- assert(rv == STDERR_FILENO);
-
-
- /* Go read the file */
- char buf[80] = { 0 };
- FILE *efile = fopen(tmpl, "r");
- assert(efile);
- char *prv = fgets(buf, sizeof(buf), efile);
- assert(prv);
- fclose(efile);
-
- unlink(tmpl);
-
- char expected[80] = { 0 };
- snprintf(expected, sizeof(expected),
- "Old McDonald had a farm. EI EIO: %s\n", strerror(EIO));
-
- /*
- fprintf(stderr,
- "\nExpected: ``%s''"
- "\nGot: ``%s''\n", expected, buf);
- */
-
- return strcmp(expected, buf) == 0 ? TEST_PASS : TEST_FAIL;
-}
-
-static void send_ascii_command(const char *buf) {
- off_t offset = 0;
- const char* ptr = buf;
- size_t len = strlen(buf);
-
- do {
- ssize_t nw = write(sock, ptr + offset, len - offset);
- if (nw == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to write: %s\n", strerror(errno));
- abort();
- }
- } else {
- offset += nw;
- }
- } while (offset < len);
-}
-
-/*
- * This is a dead slow single byte read, but it should only read out
- * _one_ response and I don't have an input buffer... The current
- * implementation only supports single-line responses, so if you want to use
- * it for get commands you need to implement that first ;-)
- */
-static void read_ascii_response(char *buffer, size_t size) {
- off_t offset = 0;
- bool need_more = true;
- do {
- ssize_t nr = read(sock, buffer + offset, 1);
- if (nr == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to read: %s\n", strerror(errno));
- abort();
- }
- } else {
- assert(nr == 1);
- if (buffer[offset] == '\n') {
- need_more = false;
- buffer[offset + 1] = '\0';
- }
- offset += nr;
- assert(offset + 1 < size);
- }
- } while (need_more);
-}
-
-static enum test_return test_issue_92(void) {
- char buffer[1024];
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- send_ascii_command("stats cachedump 1 0 0\r\n");
- read_ascii_response(buffer, sizeof(buffer));
- assert(strncmp(buffer, "END", strlen("END")) == 0);
-
- send_ascii_command("stats cachedump 200 0 0\r\n");
- read_ascii_response(buffer, sizeof(buffer));
- assert(strncmp(buffer, "CLIENT_ERROR", strlen("CLIENT_ERROR")) == 0);
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
- return TEST_PASS;
-}
-
-static enum test_return test_issue_102(void) {
- char buffer[4096];
- memset(buffer, ' ', sizeof(buffer));
- buffer[sizeof(buffer) - 1] = '\0';
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- send_ascii_command(buffer);
- /* verify that the server closed the connection */
- assert(read(sock, buffer, sizeof(buffer)) == 0);
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- snprintf(buffer, sizeof(buffer), "gets ");
- size_t offset = 5;
- while (offset < 4000) {
- offset += snprintf(buffer + offset, sizeof(buffer) - offset,
- "%010u ", (unsigned int)offset);
- }
-
- send_ascii_command(buffer);
- usleep(250);
-
- send_ascii_command("\r\n");
- char rsp[80];
- read_ascii_response(rsp, sizeof(rsp));
- assert(strncmp(rsp, "END", strlen("END")) == 0);
- buffer[3]= ' ';
- send_ascii_command(buffer);
- usleep(250);
- send_ascii_command("\r\n");
- read_ascii_response(rsp, sizeof(rsp));
- assert(strncmp(rsp, "END", strlen("END")) == 0);
-
- memset(buffer, ' ', sizeof(buffer));
- int len = snprintf(buffer + 101, sizeof(buffer) - 101, "gets foo");
- buffer[101 + len] = ' ';
- buffer[sizeof(buffer) - 1] = '\0';
- send_ascii_command(buffer);
- /* verify that the server closed the connection */
- assert(read(sock, buffer, sizeof(buffer)) == 0);
-
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- return TEST_PASS;
-}
-
-static enum test_return start_memcached_server(void) {
- server_pid = start_server(&port, false, 600);
- sock = connect_server("127.0.0.1", port, false);
- return TEST_PASS;
-}
-
-static enum test_return stop_memcached_server(void) {
- close(sock);
- assert(kill(server_pid, SIGTERM) == 0);
- return TEST_PASS;
-}
-
-static void safe_send(const void* buf, size_t len, bool hickup)
-{
- off_t offset = 0;
- const char* ptr = buf;
-#ifdef MESSAGE_DEBUG
- uint8_t val = *ptr;
- assert(val == (uint8_t)0x80);
- fprintf(stderr, "About to send %lu bytes:", (unsigned long)len);
- for (int ii = 0; ii < len; ++ii) {
- if (ii % 4 == 0) {
- fprintf(stderr, "\n ");
- }
- val = *(ptr + ii);
- fprintf(stderr, " 0x%02x", val);
- }
- fprintf(stderr, "\n");
- usleep(500);
-#endif
-
- do {
- size_t num_bytes = len - offset;
- if (hickup) {
- if (num_bytes > 1024) {
- num_bytes = (rand() % 1023) + 1;
- }
- }
-
- ssize_t nw = write(sock, ptr + offset, num_bytes);
- if (nw == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to write: %s\n", strerror(errno));
- abort();
- }
- } else {
- if (hickup) {
- usleep(100);
- }
- offset += nw;
- }
- } while (offset < len);
-}
-
-static bool safe_recv(void *buf, size_t len) {
- if (len == 0) {
- return true;
- }
- off_t offset = 0;
- do {
- ssize_t nr = read(sock, ((char*)buf) + offset, len - offset);
- if (nr == -1) {
- if (errno != EINTR) {
- fprintf(stderr, "Failed to read: %s\n", strerror(errno));
- abort();
- }
- } else {
- if (nr == 0 && allow_closed_read) {
- return false;
- }
- assert(nr != 0);
- offset += nr;
- }
- } while (offset < len);
-
- return true;
-}
-
-static bool safe_recv_packet(void *buf, size_t size) {
- protocol_binary_response_no_extras *response = buf;
- assert(size > sizeof(*response));
- if (!safe_recv(response, sizeof(*response))) {
- return false;
- }
- response->message.header.response.keylen = ntohs(response->message.header.response.keylen);
- response->message.header.response.status = ntohs(response->message.header.response.status);
- response->message.header.response.bodylen = ntohl(response->message.header.response.bodylen);
-
- size_t len = sizeof(*response);
-
- char *ptr = buf;
- ptr += len;
- if (!safe_recv(ptr, response->message.header.response.bodylen)) {
- return false;
- }
-
-#ifdef MESSAGE_DEBUG
- usleep(500);
- ptr = buf;
- len += response->message.header.response.bodylen;
- uint8_t val = *ptr;
- assert(val == (uint8_t)0x81);
- fprintf(stderr, "Received %lu bytes:", (unsigned long)len);
- for (int ii = 0; ii < len; ++ii) {
- if (ii % 4 == 0) {
- fprintf(stderr, "\n ");
- }
- val = *(ptr + ii);
- fprintf(stderr, " 0x%02x", val);
- }
- fprintf(stderr, "\n");
-#endif
- return true;
-}
-
-static off_t storage_command(char*buf,
- size_t bufsz,
- uint8_t cmd,
- const void* key,
- size_t keylen,
- const void* dta,
- size_t dtalen,
- uint32_t flags,
- uint32_t exp) {
- /* all of the storage commands use the same command layout */
- protocol_binary_request_set *request = (void*)buf;
- assert(bufsz > sizeof(*request) + keylen + dtalen);
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.extlen = 8;
- request->message.header.request.bodylen = htonl(keylen + 8 + dtalen);
- request->message.header.request.opaque = 0xdeadbeef;
- request->message.body.flags = flags;
- request->message.body.expiration = exp;
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras) + 8;
-
- memcpy(buf + key_offset, key, keylen);
- if (dta != NULL) {
- memcpy(buf + key_offset + keylen, dta, dtalen);
- }
-
- return key_offset + keylen + dtalen;
-}
-
-static off_t raw_command(char* buf,
- size_t bufsz,
- uint8_t cmd,
- const void* key,
- size_t keylen,
- const void* dta,
- size_t dtalen) {
- /* all of the storage commands use the same command layout */
- protocol_binary_request_no_extras *request = (void*)buf;
- assert(bufsz > sizeof(*request) + keylen + dtalen);
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.bodylen = htonl(keylen + dtalen);
- request->message.header.request.opaque = 0xdeadbeef;
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras);
-
- if (key != NULL) {
- memcpy(buf + key_offset, key, keylen);
- }
- if (dta != NULL) {
- memcpy(buf + key_offset + keylen, dta, dtalen);
- }
-
- return sizeof(*request) + keylen + dtalen;
-}
-
-static off_t flush_command(char* buf, size_t bufsz, uint8_t cmd, uint32_t exptime, bool use_extra) {
- protocol_binary_request_flush *request = (void*)buf;
- assert(bufsz > sizeof(*request));
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
-
- off_t size = sizeof(protocol_binary_request_no_extras);
- if (use_extra) {
- request->message.header.request.extlen = 4;
- request->message.body.expiration = htonl(exptime);
- request->message.header.request.bodylen = htonl(4);
- size += 4;
- }
-
- request->message.header.request.opaque = 0xdeadbeef;
-
- return size;
-}
-
-
-static off_t touch_command(char* buf,
- size_t bufsz,
- uint8_t cmd,
- const void* key,
- size_t keylen,
- uint32_t exptime) {
- protocol_binary_request_touch *request = (void*)buf;
- assert(bufsz > sizeof(*request));
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
-
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.extlen = 4;
- request->message.body.expiration = htonl(exptime);
- request->message.header.request.bodylen = htonl(keylen + 4);
-
- request->message.header.request.opaque = 0xdeadbeef;
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras) + 4;
-
- memcpy(buf + key_offset, key, keylen);
- return sizeof(protocol_binary_request_no_extras) + 4 + keylen;
-}
-
-static off_t arithmetic_command(char* buf,
- size_t bufsz,
- uint8_t cmd,
- const void* key,
- size_t keylen,
- uint64_t delta,
- uint64_t initial,
- uint32_t exp) {
- protocol_binary_request_incr *request = (void*)buf;
- assert(bufsz > sizeof(*request) + keylen);
-
- memset(request, 0, sizeof(*request));
- request->message.header.request.magic = PROTOCOL_BINARY_REQ;
- request->message.header.request.opcode = cmd;
- request->message.header.request.keylen = htons(keylen);
- request->message.header.request.extlen = 20;
- request->message.header.request.bodylen = htonl(keylen + 20);
- request->message.header.request.opaque = 0xdeadbeef;
- request->message.body.delta = htonll(delta);
- request->message.body.initial = htonll(initial);
- request->message.body.expiration = htonl(exp);
-
- off_t key_offset = sizeof(protocol_binary_request_no_extras) + 20;
-
- memcpy(buf + key_offset, key, keylen);
- return key_offset + keylen;
-}
-
-static void validate_response_header(protocol_binary_response_no_extras *response,
- uint8_t cmd, uint16_t status)
-{
- assert(response->message.header.response.magic == PROTOCOL_BINARY_RES);
- assert(response->message.header.response.opcode == cmd);
- assert(response->message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
- assert(response->message.header.response.status == status);
- assert(response->message.header.response.opaque == 0xdeadbeef);
-
- if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
- switch (cmd) {
- case PROTOCOL_BINARY_CMD_ADDQ:
- case PROTOCOL_BINARY_CMD_APPENDQ:
- case PROTOCOL_BINARY_CMD_DECREMENTQ:
- case PROTOCOL_BINARY_CMD_DELETEQ:
- case PROTOCOL_BINARY_CMD_FLUSHQ:
- case PROTOCOL_BINARY_CMD_INCREMENTQ:
- case PROTOCOL_BINARY_CMD_PREPENDQ:
- case PROTOCOL_BINARY_CMD_QUITQ:
- case PROTOCOL_BINARY_CMD_REPLACEQ:
- case PROTOCOL_BINARY_CMD_SETQ:
- assert("Quiet command shouldn't return on success" == NULL);
- default:
- break;
- }
-
- switch (cmd) {
- case PROTOCOL_BINARY_CMD_ADD:
- case PROTOCOL_BINARY_CMD_REPLACE:
- case PROTOCOL_BINARY_CMD_SET:
- case PROTOCOL_BINARY_CMD_APPEND:
- case PROTOCOL_BINARY_CMD_PREPEND:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen == 0);
- assert(response->message.header.response.cas != 0);
- break;
- case PROTOCOL_BINARY_CMD_FLUSH:
- case PROTOCOL_BINARY_CMD_NOOP:
- case PROTOCOL_BINARY_CMD_QUIT:
- case PROTOCOL_BINARY_CMD_DELETE:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen == 0);
- assert(response->message.header.response.cas == 0);
- break;
-
- case PROTOCOL_BINARY_CMD_DECREMENT:
- case PROTOCOL_BINARY_CMD_INCREMENT:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen == 8);
- assert(response->message.header.response.cas != 0);
- break;
-
- case PROTOCOL_BINARY_CMD_STAT:
- assert(response->message.header.response.extlen == 0);
- /* key and value exists in all packets except in the terminating */
- assert(response->message.header.response.cas == 0);
- break;
-
- case PROTOCOL_BINARY_CMD_VERSION:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 0);
- assert(response->message.header.response.bodylen != 0);
- assert(response->message.header.response.cas == 0);
- break;
-
- case PROTOCOL_BINARY_CMD_GET:
- case PROTOCOL_BINARY_CMD_GETQ:
- assert(response->message.header.response.keylen == 0);
- assert(response->message.header.response.extlen == 4);
- assert(response->message.header.response.cas != 0);
- break;
-
- case PROTOCOL_BINARY_CMD_GETK:
- case PROTOCOL_BINARY_CMD_GETKQ:
- assert(response->message.header.response.keylen != 0);
- assert(response->message.header.response.extlen == 4);
- assert(response->message.header.response.cas != 0);
- break;
-
- default:
- /* Undefined command code */
- break;
- }
- } else {
- assert(response->message.header.response.cas == 0);
- assert(response->message.header.response.extlen == 0);
- if (cmd != PROTOCOL_BINARY_CMD_GETK &&
- cmd != PROTOCOL_BINARY_CMD_GATK) {
- assert(response->message.header.response.keylen == 0);
- }
- }
-}
-
-static enum test_return test_binary_noop(void) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
-
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- PROTOCOL_BINARY_CMD_NOOP,
- NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_NOOP,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_quit_impl(uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- cmd, NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_QUIT) {
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_QUIT,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- /* Socket should be closed now, read should return 0 */
- assert(read(sock, buffer.bytes, sizeof(buffer.bytes)) == 0);
- close(sock);
- sock = connect_server("127.0.0.1", port, false);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_quit(void) {
- return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
-}
-
-static enum test_return test_binary_quitq(void) {
- return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
-}
-
-static enum test_return test_binary_set_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- uint64_t value = 0xdeadbeefdeadcafe;
- size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), &value, sizeof(value),
- 0, 0);
-
- /* Set should work over and over again */
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_SET) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
- }
-
- if (cmd == PROTOCOL_BINARY_CMD_SETQ) {
- return test_binary_noop();
- }
-
- send.request.message.header.request.cas = receive.response.message.header.response.cas;
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_SET) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(receive.response.message.header.response.cas != send.request.message.header.request.cas);
- } else {
- return test_binary_noop();
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_set(void) {
- return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
-}
-
-static enum test_return test_binary_setq(void) {
- return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
-}
-
-
-static enum test_return test_binary_add_impl(const char *key, uint8_t cmd) {
- uint64_t value = 0xdeadbeefdeadcafe;
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd, key,
- strlen(key), &value, sizeof(value),
- 0, 0);
-
- /* Add should only work the first time */
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (ii == 0) {
- if (cmd == PROTOCOL_BINARY_CMD_ADD) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
- } else {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
- }
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_add(void) {
- return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
-}
-
-static enum test_return test_binary_addq(void) {
- return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
-}
-
-static enum test_return test_binary_replace_impl(const char* key, uint8_t cmd) {
- uint64_t value = 0xdeadbeefdeadcafe;
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = storage_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), &value, sizeof(value),
- 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), &value, sizeof(value), 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = storage_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), &value, sizeof(value), 0, 0);
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_REPLACE) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response,
- PROTOCOL_BINARY_CMD_REPLACE,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
- }
-
- if (cmd == PROTOCOL_BINARY_CMD_REPLACEQ) {
- test_binary_noop();
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_replace(void) {
- return test_binary_replace_impl("test_binary_replace",
- PROTOCOL_BINARY_CMD_REPLACE);
-}
-
-static enum test_return test_binary_replaceq(void) {
- return test_binary_replace_impl("test_binary_replaceq",
- PROTOCOL_BINARY_CMD_REPLACEQ);
-}
-
-static enum test_return test_binary_delete_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), NULL, 0);
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0, 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = raw_command(send.bytes, sizeof(send.bytes),
- cmd, key, strlen(key), NULL, 0);
- safe_send(send.bytes, len, false);
-
- if (cmd == PROTOCOL_BINARY_CMD_DELETE) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_DELETE,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_delete(void) {
- return test_binary_delete_impl("test_binary_delete",
- PROTOCOL_BINARY_CMD_DELETE);
-}
-
-static enum test_return test_binary_deleteq(void) {
- return test_binary_delete_impl("test_binary_deleteq",
- PROTOCOL_BINARY_CMD_DELETEQ);
-}
-
-static enum test_return test_binary_get_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), NULL, 0);
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0,
- 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- /* run a little pipeline test ;-) */
- len = 0;
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- union {
- protocol_binary_request_no_extras request;
- char bytes[1024];
- } temp;
- size_t l = raw_command(temp.bytes, sizeof(temp.bytes),
- cmd, key, strlen(key), NULL, 0);
- memcpy(send.bytes + len, temp.bytes, l);
- len += l;
- }
-
- safe_send(send.bytes, len, false);
- for (ii = 0; ii < 10; ++ii) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_get(void) {
- return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
-}
-
-static enum test_return test_binary_getk(void) {
- return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
-}
-
-static enum test_return test_binary_getq_impl(const char *key, uint8_t cmd) {
- const char *missing = "test_binary_getq_missing";
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, temp, receive;
- size_t len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0,
- 0, 0);
- size_t len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
- missing, strlen(missing), NULL, 0);
- /* I need to change the first opaque so that I can separate the two
- * return packets */
- temp.request.message.header.request.opaque = 0xfeedface;
- memcpy(send.bytes + len, temp.bytes, len2);
- len += len2;
-
- len2 = raw_command(temp.bytes, sizeof(temp.bytes), cmd,
- key, strlen(key), NULL, 0);
- memcpy(send.bytes + len, temp.bytes, len2);
- len += len2;
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- /* The first GETQ shouldn't return anything */
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_getq(void) {
- return test_binary_getq_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
-}
-
-static enum test_return test_binary_getkq(void) {
- return test_binary_getq_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
-}
-
-static enum test_return test_binary_incr_impl(const char* key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response_header;
- protocol_binary_response_incr response;
- char bytes[1024];
- } send, receive;
- size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), 1, 0, 0);
-
- int ii;
- for (ii = 0; ii < 10; ++ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response_header, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(ntohll(receive.response.message.body.value) == ii);
- }
- }
-
- if (cmd == PROTOCOL_BINARY_CMD_INCREMENTQ) {
- test_binary_noop();
- }
- return TEST_PASS;
-}
-
-static enum test_return test_binary_incr(void) {
- return test_binary_incr_impl("test_binary_incr",
- PROTOCOL_BINARY_CMD_INCREMENT);
-}
-
-static enum test_return test_binary_incrq(void) {
- return test_binary_incr_impl("test_binary_incrq",
- PROTOCOL_BINARY_CMD_INCREMENTQ);
-}
-
-static enum test_return test_binary_decr_impl(const char* key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response_header;
- protocol_binary_response_decr response;
- char bytes[1024];
- } send, receive;
- size_t len = arithmetic_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), 1, 9, 0);
-
- int ii;
- for (ii = 9; ii >= 0; --ii) {
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response_header, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(ntohll(receive.response.message.body.value) == ii);
- }
- }
-
- /* decr on 0 should not wrap */
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_DECREMENT) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response_header, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- assert(ntohll(receive.response.message.body.value) == 0);
- } else {
- test_binary_noop();
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_decr(void) {
- return test_binary_decr_impl("test_binary_decr",
- PROTOCOL_BINARY_CMD_DECREMENT);
-}
-
-static enum test_return test_binary_decrq(void) {
- return test_binary_decr_impl("test_binary_decrq",
- PROTOCOL_BINARY_CMD_DECREMENTQ);
-}
-
-static enum test_return test_binary_version(void) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
-
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- PROTOCOL_BINARY_CMD_VERSION,
- NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_VERSION,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_flush_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
-
- size_t len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0, 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = flush_command(send.bytes, sizeof(send.bytes), cmd, 2, true);
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GET,
- key, strlen(key), NULL, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- sleep(2);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-
- int ii;
- for (ii = 0; ii < 2; ++ii) {
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), NULL, 0, 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = flush_command(send.bytes, sizeof(send.bytes), cmd, 0, ii == 0);
- safe_send(send.bytes, len, false);
- if (cmd == PROTOCOL_BINARY_CMD_FLUSH) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- len = raw_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_GET,
- key, strlen(key), NULL, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GET,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
- }
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_flush(void) {
- return test_binary_flush_impl("test_binary_flush",
- PROTOCOL_BINARY_CMD_FLUSH);
-}
-
-static enum test_return test_binary_flushq(void) {
- return test_binary_flush_impl("test_binary_flushq",
- PROTOCOL_BINARY_CMD_FLUSHQ);
-}
-
-static enum test_return test_binary_concat_impl(const char *key, uint8_t cmd) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } send, receive;
- const char *value = "world";
-
- size_t len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), value, strlen(value));
-
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_NOT_STORED);
-
- len = storage_command(send.bytes, sizeof(send.bytes),
- PROTOCOL_BINARY_CMD_ADD,
- key, strlen(key), value, strlen(value), 0, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_ADD,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- len = raw_command(send.bytes, sizeof(send.bytes), cmd,
- key, strlen(key), value, strlen(value));
- safe_send(send.bytes, len, false);
-
- if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_PREPEND) {
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, cmd,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- } else {
- len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_NOOP,
- NULL, 0, NULL, 0);
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_NOOP,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- }
-
- len = raw_command(send.bytes, sizeof(send.bytes), PROTOCOL_BINARY_CMD_GETK,
- key, strlen(key), NULL, 0);
-
- safe_send(send.bytes, len, false);
- safe_recv_packet(receive.bytes, sizeof(receive.bytes));
- validate_response_header(&receive.response, PROTOCOL_BINARY_CMD_GETK,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
-
- assert(receive.response.message.header.response.keylen == strlen(key));
- assert(receive.response.message.header.response.bodylen == (strlen(key) + 2*strlen(value) + 4));
-
- char *ptr = receive.bytes;
- ptr += sizeof(receive.response);
- ptr += 4;
-
- assert(memcmp(ptr, key, strlen(key)) == 0);
- ptr += strlen(key);
- assert(memcmp(ptr, value, strlen(value)) == 0);
- ptr += strlen(value);
- assert(memcmp(ptr, value, strlen(value)) == 0);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_append(void) {
- return test_binary_concat_impl("test_binary_append",
- PROTOCOL_BINARY_CMD_APPEND);
-}
-
-static enum test_return test_binary_prepend(void) {
- return test_binary_concat_impl("test_binary_prepend",
- PROTOCOL_BINARY_CMD_PREPEND);
-}
-
-static enum test_return test_binary_appendq(void) {
- return test_binary_concat_impl("test_binary_appendq",
- PROTOCOL_BINARY_CMD_APPENDQ);
-}
-
-static enum test_return test_binary_prependq(void) {
- return test_binary_concat_impl("test_binary_prependq",
- PROTOCOL_BINARY_CMD_PREPENDQ);
-}
-
-static enum test_return test_binary_stat(void) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
-
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- PROTOCOL_BINARY_CMD_STAT,
- NULL, 0, NULL, 0);
-
- safe_send(buffer.bytes, len, false);
- do {
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, PROTOCOL_BINARY_CMD_STAT,
- PROTOCOL_BINARY_RESPONSE_SUCCESS);
- } while (buffer.response.message.header.response.keylen != 0);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_illegal(void) {
- uint8_t cmd = 0x25;
- while (cmd != 0x00) {
- union {
- protocol_binary_request_no_extras request;
- protocol_binary_response_no_extras response;
- char bytes[1024];
- } buffer;
- size_t len = raw_command(buffer.bytes, sizeof(buffer.bytes),
- cmd, NULL, 0, NULL, 0);
- safe_send(buffer.bytes, len, false);
- safe_recv_packet(buffer.bytes, sizeof(buffer.bytes));
- validate_response_header(&buffer.response, cmd,
- PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
- ++cmd;
- }
-
- return TEST_PASS;
-}
-
-volatile bool hickup_thread_running;
-
-static void *binary_hickup_recv_verification_thread(void *arg) {
- protocol_binary_response_no_extras *response = malloc(65*1024);
- if (response != NULL) {
- while (safe_recv_packet(response, 65*1024)) {
- /* Just validate the packet format */
- validate_response_header(response,
- response->message.header.response.opcode,
- response->message.header.response.status);
- }
- free(response);
- }
- hickup_thread_running = false;
- allow_closed_read = false;
- return NULL;
-}
-
-static enum test_return test_binary_pipeline_hickup_chunk(void *buffer, size_t buffersize) {
- off_t offset = 0;
- char *key[256];
- uint64_t value = 0xfeedfacedeadbeef;
-
- while (hickup_thread_running &&
- offset + sizeof(protocol_binary_request_no_extras) < buffersize) {
- union {
- protocol_binary_request_no_extras request;
- char bytes[65 * 1024];
- } command;
- uint8_t cmd = (uint8_t)(rand() & 0xff);
- size_t len;
- size_t keylen = (rand() % 250) + 1;
-
- switch (cmd) {
- case PROTOCOL_BINARY_CMD_ADD:
- case PROTOCOL_BINARY_CMD_ADDQ:
- case PROTOCOL_BINARY_CMD_REPLACE:
- case PROTOCOL_BINARY_CMD_REPLACEQ:
- case PROTOCOL_BINARY_CMD_SET:
- case PROTOCOL_BINARY_CMD_SETQ:
- len = storage_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen , &value, sizeof(value),
- 0, 0);
- break;
- case PROTOCOL_BINARY_CMD_APPEND:
- case PROTOCOL_BINARY_CMD_APPENDQ:
- case PROTOCOL_BINARY_CMD_PREPEND:
- case PROTOCOL_BINARY_CMD_PREPENDQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, &value, sizeof(value));
- break;
- case PROTOCOL_BINARY_CMD_FLUSH:
- case PROTOCOL_BINARY_CMD_FLUSHQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- NULL, 0, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_NOOP:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- NULL, 0, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_DELETE:
- case PROTOCOL_BINARY_CMD_DELETEQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_DECREMENT:
- case PROTOCOL_BINARY_CMD_DECREMENTQ:
- case PROTOCOL_BINARY_CMD_INCREMENT:
- case PROTOCOL_BINARY_CMD_INCREMENTQ:
- len = arithmetic_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, 1, 0, 0);
- break;
- case PROTOCOL_BINARY_CMD_VERSION:
- len = raw_command(command.bytes, sizeof(command.bytes),
- PROTOCOL_BINARY_CMD_VERSION,
- NULL, 0, NULL, 0);
- break;
- case PROTOCOL_BINARY_CMD_GET:
- case PROTOCOL_BINARY_CMD_GETK:
- case PROTOCOL_BINARY_CMD_GETKQ:
- case PROTOCOL_BINARY_CMD_GETQ:
- len = raw_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, NULL, 0);
- break;
-
- case PROTOCOL_BINARY_CMD_TOUCH:
- case PROTOCOL_BINARY_CMD_GAT:
- case PROTOCOL_BINARY_CMD_GATQ:
- case PROTOCOL_BINARY_CMD_GATK:
- case PROTOCOL_BINARY_CMD_GATKQ:
- len = touch_command(command.bytes, sizeof(command.bytes), cmd,
- key, keylen, 10);
- break;
-
- case PROTOCOL_BINARY_CMD_STAT:
- len = raw_command(command.bytes, sizeof(command.bytes),
- PROTOCOL_BINARY_CMD_STAT,
- NULL, 0, NULL, 0);
- break;
-
- case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
- case PROTOCOL_BINARY_CMD_SASL_AUTH:
- case PROTOCOL_BINARY_CMD_SASL_STEP:
- /* Ignoring SASL */
- case PROTOCOL_BINARY_CMD_QUITQ:
- case PROTOCOL_BINARY_CMD_QUIT:
- /* I don't want to pass on the quit commands ;-) */
- cmd |= 0xf0;
- /* FALLTHROUGH */
- default:
- len = raw_command(command.bytes, sizeof(command.bytes),
- cmd, NULL, 0, NULL, 0);
- }
-
- if ((len + offset) < buffersize) {
- memcpy(((char*)buffer) + offset, command.bytes, len);
- offset += len;
- } else {
- break;
- }
- }
- safe_send(buffer, offset, true);
-
- return TEST_PASS;
-}
-
-static enum test_return test_binary_pipeline_hickup(void)
-{
- size_t buffersize = 65 * 1024;
- void *buffer = malloc(buffersize);
- int ii;
-
- pthread_t tid;
- int ret;
- allow_closed_read = true;
- hickup_thread_running = true;
- if ((ret = pthread_create(&tid, NULL,
- binary_hickup_recv_verification_thread, NULL)) != 0) {
- fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
- return TEST_FAIL;
- }
-
- /* Allow the thread to start */
- usleep(250);
-
- srand((int)time(NULL));
- for (ii = 0; ii < 2; ++ii) {
- test_binary_pipeline_hickup_chunk(buffer, buffersize);
- }
-
- /* send quitq to shut down the read thread ;-) */
- size_t len = raw_command(buffer, buffersize, PROTOCOL_BINARY_CMD_QUITQ,
- NULL, 0, NULL, 0);
- safe_send(buffer, len, false);
-
- pthread_join(tid, NULL);
- free(buffer);
- return TEST_PASS;
-}
-
-
-static enum test_return test_issue_101(void) {
- const int max = 2;
- enum test_return ret = TEST_PASS;
- int fds[max];
- int ii = 0;
- pid_t child = 0;
-
- if (getenv("SKIP_TEST_101") != NULL) {
- return TEST_SKIP;
- }
-
- const char *command = "stats\r\nstats\r\nstats\r\nstats\r\nstats\r\n";
- size_t cmdlen = strlen(command);
-
- server_pid = start_server(&port, false, 1000);
-
- for (ii = 0; ii < max; ++ii) {
- fds[ii] = connect_server("127.0.0.1", port, true);
- assert(fds[ii] > 0);
- }
-
- /* Send command on the connection until it blocks */
- for (ii = 0; ii < max; ++ii) {
- bool more = true;
- do {
- ssize_t err = write(fds[ii], command, cmdlen);
- if (err == -1) {
- switch (errno) {
- case EINTR:
- break;
- case ENOMEM:
- case EWOULDBLOCK:
- more = false;
- break;
- default:
- ret = TEST_FAIL;
- goto cleanup;
- }
- }
- } while (more);
- }
-
- child = fork();
- if (child == (pid_t)-1) {
- abort();
- } else if (child > 0) {
- int stat;
- pid_t c;
- while ((c = waitpid(child, &stat, 0)) == (pid_t)-1 && errno == EINTR);
- assert(c == child);
- assert(stat == 0);
- } else {
- sock = connect_server("127.0.0.1", port, false);
- ret = test_binary_noop();
- close(sock);
- exit(0);
- }
-
- cleanup:
- /* close all connections */
- for (ii = 0; ii < max; ++ii) {
- close(fds[ii]);
- }
-
- assert(kill(server_pid, SIGTERM) == 0);
-
- return ret;
-}
-
-typedef enum test_return (*TEST_FUNC)(void);
-struct testcase {
- const char *description;
- TEST_FUNC function;
-};
-
-struct testcase testcases[] = {
- { "cache_create", cache_create_test },
- { "cache_constructor", cache_constructor_test },
- { "cache_constructor_fail", cache_fail_constructor_test },
- { "cache_destructor", cache_destructor_test },
- { "cache_reuse", cache_reuse_test },
- { "cache_redzone", cache_redzone_test },
- { "issue_161", test_issue_161 },
- { "strtol", test_safe_strtol },
- { "strtoll", test_safe_strtoll },
- { "strtoul", test_safe_strtoul },
- { "strtoull", test_safe_strtoull },
- { "issue_44", test_issue_44 },
- { "vperror", test_vperror },
- { "issue_101", test_issue_101 },
- /* The following tests all run towards the same server */
- { "start_server", start_memcached_server },
- { "issue_92", test_issue_92 },
- { "issue_102", test_issue_102 },
- { "binary_noop", test_binary_noop },
- { "binary_quit", test_binary_quit },
- { "binary_quitq", test_binary_quitq },
- { "binary_set", test_binary_set },
- { "binary_setq", test_binary_setq },
- { "binary_add", test_binary_add },
- { "binary_addq", test_binary_addq },
- { "binary_replace", test_binary_replace },
- { "binary_replaceq", test_binary_replaceq },
- { "binary_delete", test_binary_delete },
- { "binary_deleteq", test_binary_deleteq },
- { "binary_get", test_binary_get },
- { "binary_getq", test_binary_getq },
- { "binary_getk", test_binary_getk },
- { "binary_getkq", test_binary_getkq },
- { "binary_incr", test_binary_incr },
- { "binary_incrq", test_binary_incrq },
- { "binary_decr", test_binary_decr },
- { "binary_decrq", test_binary_decrq },
- { "binary_version", test_binary_version },
- { "binary_flush", test_binary_flush },
- { "binary_flushq", test_binary_flushq },
- { "binary_append", test_binary_append },
- { "binary_appendq", test_binary_appendq },
- { "binary_prepend", test_binary_prepend },
- { "binary_prependq", test_binary_prependq },
- { "binary_stat", test_binary_stat },
- { "binary_illegal", test_binary_illegal },
- { "binary_pipeline_hickup", test_binary_pipeline_hickup },
- { "stop_server", stop_memcached_server },
- { NULL, NULL }
-};
-
-int main(int argc, char **argv)
-{
- int exitcode = 0;
- int ii = 0, num_cases = 0;
-
- for (num_cases = 0; testcases[num_cases].description; num_cases++) {
- /* Just counting */
- }
-
- printf("1..%d\n", num_cases);
-
- for (ii = 0; testcases[ii].description != NULL; ++ii) {
- fflush(stdout);
-#ifndef DEBUG
- /* the test program shouldn't run longer than 10 minutes... */
- alarm(600);
-#endif
- enum test_return ret = testcases[ii].function();
- if (ret == TEST_SKIP) {
- fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
- } else if (ret == TEST_PASS) {
- fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description);
- } else {
- fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description);
- exitcode = 1;
- }
- fflush(stdout);
- }
-
- return exitcode;
-}
+++ /dev/null
-/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- * Thread management for memcached.
- */
-#include "memcached.h"
-#include <assert.h>
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <pthread.h>
-
-#ifdef __sun
-#include <atomic.h>
-#endif
-
-#define ITEMS_PER_ALLOC 64
-
-/* An item in the connection queue. */
-typedef struct conn_queue_item CQ_ITEM;
-struct conn_queue_item {
- int sfd;
- enum conn_states init_state;
- int event_flags;
- int read_buffer_size;
- enum network_transport transport;
- CQ_ITEM *next;
-};
-
-/* A connection queue. */
-typedef struct conn_queue CQ;
-struct conn_queue {
- CQ_ITEM *head;
- CQ_ITEM *tail;
- pthread_mutex_t lock;
- pthread_cond_t cond;
-};
-
-/* Lock for cache operations (item_*, assoc_*) */
-pthread_mutex_t cache_lock;
-
-/* Connection lock around accepting new connections */
-pthread_mutex_t conn_lock = PTHREAD_MUTEX_INITIALIZER;
-
-#if !defined(HAVE_GCC_ATOMICS) && !defined(__sun)
-pthread_mutex_t atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
-#endif
-
-/* Lock for global stats */
-static pthread_mutex_t stats_lock;
-
-/* Free list of CQ_ITEM structs */
-static CQ_ITEM *cqi_freelist;
-static pthread_mutex_t cqi_freelist_lock;
-
-static pthread_mutex_t *item_locks;
-/* size of the item lock hash table */
-static uint32_t item_lock_count;
-/* size - 1 for lookup masking */
-static uint32_t item_lock_mask;
-
-static LIBEVENT_DISPATCHER_THREAD dispatcher_thread;
-
-/*
- * Each libevent instance has a wakeup pipe, which other threads
- * can use to signal that they've put a new connection on its queue.
- */
-static LIBEVENT_THREAD *threads;
-
-/*
- * Number of worker threads that have finished setting themselves up.
- */
-static int init_count = 0;
-static pthread_mutex_t init_lock;
-static pthread_cond_t init_cond;
-
-
-static void thread_libevent_process(int fd, short which, void *arg);
-
-unsigned short refcount_incr(unsigned short *refcount) {
-#ifdef HAVE_GCC_ATOMICS
- return __sync_add_and_fetch(refcount, 1);
-#elif defined(__sun)
- return atomic_inc_ushort_nv(refcount);
-#else
- unsigned short res;
- mutex_lock(&atomics_mutex);
- (*refcount)++;
- res = *refcount;
- mutex_unlock(&atomics_mutex);
- return res;
-#endif
-}
-
-unsigned short refcount_decr(unsigned short *refcount) {
-#ifdef HAVE_GCC_ATOMICS
- return __sync_sub_and_fetch(refcount, 1);
-#elif defined(__sun)
- return atomic_dec_ushort_nv(refcount);
-#else
- unsigned short res;
- mutex_lock(&atomics_mutex);
- (*refcount)--;
- res = *refcount;
- mutex_unlock(&atomics_mutex);
- return res;
-#endif
-}
-
-void item_lock(uint32_t hv) {
- mutex_lock(&item_locks[hv & item_lock_mask]);
-}
-
-void item_unlock(uint32_t hv) {
- mutex_unlock(&item_locks[hv & item_lock_mask]);
-}
-
-/*
- * Initializes a connection queue.
- */
-static void cq_init(CQ *cq) {
- pthread_mutex_init(&cq->lock, NULL);
- pthread_cond_init(&cq->cond, NULL);
- cq->head = NULL;
- cq->tail = NULL;
-}
-
-/*
- * Looks for an item on a connection queue, but doesn't block if there isn't
- * one.
- * Returns the item, or NULL if no item is available
- */
-static CQ_ITEM *cq_pop(CQ *cq) {
- CQ_ITEM *item;
-
- pthread_mutex_lock(&cq->lock);
- item = cq->head;
- if (NULL != item) {
- cq->head = item->next;
- if (NULL == cq->head)
- cq->tail = NULL;
- }
- pthread_mutex_unlock(&cq->lock);
-
- return item;
-}
-
-/*
- * Adds an item to a connection queue.
- */
-static void cq_push(CQ *cq, CQ_ITEM *item) {
- item->next = NULL;
-
- pthread_mutex_lock(&cq->lock);
- if (NULL == cq->tail)
- cq->head = item;
- else
- cq->tail->next = item;
- cq->tail = item;
- pthread_cond_signal(&cq->cond);
- pthread_mutex_unlock(&cq->lock);
-}
-
-/*
- * Returns a fresh connection queue item.
- */
-static CQ_ITEM *cqi_new(void) {
- CQ_ITEM *item = NULL;
- pthread_mutex_lock(&cqi_freelist_lock);
- if (cqi_freelist) {
- item = cqi_freelist;
- cqi_freelist = item->next;
- }
- pthread_mutex_unlock(&cqi_freelist_lock);
-
- if (NULL == item) {
- int i;
-
- /* Allocate a bunch of items at once to reduce fragmentation */
- item = malloc(sizeof(CQ_ITEM) * ITEMS_PER_ALLOC);
- if (NULL == item)
- return NULL;
-
- /*
- * Link together all the new items except the first one
- * (which we'll return to the caller) for placement on
- * the freelist.
- */
- for (i = 2; i < ITEMS_PER_ALLOC; i++)
- item[i - 1].next = &item[i];
-
- pthread_mutex_lock(&cqi_freelist_lock);
- item[ITEMS_PER_ALLOC - 1].next = cqi_freelist;
- cqi_freelist = &item[1];
- pthread_mutex_unlock(&cqi_freelist_lock);
- }
-
- return item;
-}
-
-
-/*
- * Frees a connection queue item (adds it to the freelist.)
- */
-static void cqi_free(CQ_ITEM *item) {
- pthread_mutex_lock(&cqi_freelist_lock);
- item->next = cqi_freelist;
- cqi_freelist = item;
- pthread_mutex_unlock(&cqi_freelist_lock);
-}
-
-
-/*
- * Creates a worker thread.
- */
-static void create_worker(void *(*func)(void *), void *arg) {
- pthread_t thread;
- pthread_attr_t attr;
- int ret;
-
- pthread_attr_init(&attr);
-
- if ((ret = pthread_create(&thread, &attr, func, arg)) != 0) {
- fprintf(stderr, "Can't create thread: %s\n",
- strerror(ret));
- exit(1);
- }
-}
-
-/*
- * Sets whether or not we accept new connections.
- */
-void accept_new_conns(const bool do_accept) {
- pthread_mutex_lock(&conn_lock);
- do_accept_new_conns(do_accept);
- pthread_mutex_unlock(&conn_lock);
-}
-/****************************** LIBEVENT THREADS *****************************/
-
-/*
- * Set up a thread's information.
- */
-static void setup_thread(LIBEVENT_THREAD *me) {
- me->base = event_init();
- if (! me->base) {
- fprintf(stderr, "Can't allocate event base\n");
- exit(1);
- }
-
- /* Listen for notifications from other threads */
- event_set(&me->notify_event, me->notify_receive_fd,
- EV_READ | EV_PERSIST, thread_libevent_process, me);
- event_base_set(me->base, &me->notify_event);
-
- if (event_add(&me->notify_event, 0) == -1) {
- fprintf(stderr, "Can't monitor libevent notify pipe\n");
- exit(1);
- }
-
- me->new_conn_queue = malloc(sizeof(struct conn_queue));
- if (me->new_conn_queue == NULL) {
- perror("Failed to allocate memory for connection queue");
- exit(EXIT_FAILURE);
- }
- cq_init(me->new_conn_queue);
-
- if (pthread_mutex_init(&me->stats.mutex, NULL) != 0) {
- perror("Failed to initialize mutex");
- exit(EXIT_FAILURE);
- }
-
- me->suffix_cache = cache_create("suffix", SUFFIX_SIZE, sizeof(char*),
- NULL, NULL);
- if (me->suffix_cache == NULL) {
- fprintf(stderr, "Failed to create suffix cache\n");
- exit(EXIT_FAILURE);
- }
-}
-
-
-/*
- * Worker thread: main event loop
- */
-static void *worker_libevent(void *arg) {
- LIBEVENT_THREAD *me = arg;
-
- /* Any per-thread setup can happen here; thread_init() will block until
- * all threads have finished initializing.
- */
-
- pthread_mutex_lock(&init_lock);
- init_count++;
- pthread_cond_signal(&init_cond);
- pthread_mutex_unlock(&init_lock);
-
- event_base_loop(me->base, 0);
- return NULL;
-}
-
-
-/*
- * Processes an incoming "handle a new connection" item. This is called when
- * input arrives on the libevent wakeup pipe.
- */
-static void thread_libevent_process(int fd, short which, void *arg) {
- LIBEVENT_THREAD *me = arg;
- CQ_ITEM *item;
- char buf[1];
-
- if (read(fd, buf, 1) != 1)
- if (settings.verbose > 0)
- fprintf(stderr, "Can't read from libevent pipe\n");
-
- item = cq_pop(me->new_conn_queue);
-
- if (NULL != item) {
- conn *c = conn_new(item->sfd, item->init_state, item->event_flags,
- item->read_buffer_size, item->transport, me->base);
- if (c == NULL) {
- if (IS_UDP(item->transport)) {
- fprintf(stderr, "Can't listen for events on UDP socket\n");
- exit(1);
- } else {
- if (settings.verbose > 0) {
- fprintf(stderr, "Can't listen for events on fd %d\n",
- item->sfd);
- }
- close(item->sfd);
- }
- } else {
- c->thread = me;
- }
- cqi_free(item);
- }
-}
-
-/* Which thread we assigned a connection to most recently. */
-static int last_thread = -1;
-
-/*
- * Dispatches a new connection to another thread. This is only ever called
- * from the main thread, either during initialization (for UDP) or because
- * of an incoming connection.
- */
-void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags,
- int read_buffer_size, enum network_transport transport) {
- CQ_ITEM *item = cqi_new();
- int tid = (last_thread + 1) % settings.num_threads;
-
- LIBEVENT_THREAD *thread = threads + tid;
-
- last_thread = tid;
-
- item->sfd = sfd;
- item->init_state = init_state;
- item->event_flags = event_flags;
- item->read_buffer_size = read_buffer_size;
- item->transport = transport;
-
- cq_push(thread->new_conn_queue, item);
-
- MEMCACHED_CONN_DISPATCH(sfd, thread->thread_id);
- if (write(thread->notify_send_fd, "", 1) != 1) {
- perror("Writing to thread notify pipe");
- }
-}
-
-/*
- * Returns true if this is the thread that listens for new TCP connections.
- */
-int is_listen_thread() {
- return pthread_self() == dispatcher_thread.thread_id;
-}
-
-/********************************* ITEM ACCESS *******************************/
-
-/*
- * Allocates a new item.
- */
-item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes) {
- item *it;
- /* do_item_alloc handles its own locks */
- it = do_item_alloc(key, nkey, flags, exptime, nbytes);
- return it;
-}
-
-/*
- * Returns an item if it hasn't been marked as expired,
- * lazy-expiring as needed.
- */
-item *item_get(const char *key, const size_t nkey) {
- item *it;
- uint32_t hv;
- hv = hash(key, nkey, 0);
- item_lock(hv);
- it = do_item_get(key, nkey, hv);
- item_unlock(hv);
- return it;
-}
-
-item *item_touch(const char *key, size_t nkey, uint32_t exptime) {
- item *it;
- uint32_t hv;
- hv = hash(key, nkey, 0);
- item_lock(hv);
- it = do_item_touch(key, nkey, exptime, hv);
- item_unlock(hv);
- return it;
-}
-
-/*
- * Links an item into the LRU and hashtable.
- */
-int item_link(item *item) {
- int ret;
- uint32_t hv;
-
- hv = hash(ITEM_key(item), item->nkey, 0);
- item_lock(hv);
- ret = do_item_link(item, hv);
- item_unlock(hv);
- return ret;
-}
-
-/*
- * Decrements the reference count on an item and adds it to the freelist if
- * needed.
- */
-void item_remove(item *item) {
- uint32_t hv;
- hv = hash(ITEM_key(item), item->nkey, 0);
-
- item_lock(hv);
- do_item_remove(item);
- item_unlock(hv);
-}
-
-/*
- * Replaces one item with another in the hashtable.
- * Unprotected by a mutex lock since the core server does not require
- * it to be thread-safe.
- */
-int item_replace(item *old_it, item *new_it, const uint32_t hv) {
- return do_item_replace(old_it, new_it, hv);
-}
-
-/*
- * Unlinks an item from the LRU and hashtable.
- */
-void item_unlink(item *item) {
- uint32_t hv;
- hv = hash(ITEM_key(item), item->nkey, 0);
- item_lock(hv);
- do_item_unlink(item, hv);
- item_unlock(hv);
-}
-
-/*
- * Moves an item to the back of the LRU queue.
- */
-void item_update(item *item) {
- uint32_t hv;
- hv = hash(ITEM_key(item), item->nkey, 0);
-
- item_lock(hv);
- do_item_update(item);
- item_unlock(hv);
-}
-
-/*
- * Does arithmetic on a numeric item value.
- */
-enum delta_result_type add_delta(conn *c, const char *key,
- const size_t nkey, int incr,
- const int64_t delta, char *buf,
- uint64_t *cas) {
- enum delta_result_type ret;
- uint32_t hv;
-
- hv = hash(key, nkey, 0);
- item_lock(hv);
- ret = do_add_delta(c, key, nkey, incr, delta, buf, cas, hv);
- item_unlock(hv);
- return ret;
-}
-
-/*
- * Stores an item in the cache (high level, obeys set/add/replace semantics)
- */
-enum store_item_type store_item(item *item, int comm, conn* c) {
- enum store_item_type ret;
- uint32_t hv;
-
- hv = hash(ITEM_key(item), item->nkey, 0);
- item_lock(hv);
- ret = do_store_item(item, comm, c, hv);
- item_unlock(hv);
- return ret;
-}
-
-/*
- * Flushes expired items after a flush_all call
- */
-void item_flush_expired() {
- mutex_lock(&cache_lock);
- do_item_flush_expired();
- mutex_unlock(&cache_lock);
-}
-
-/*
- * Dumps part of the cache
- */
-char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, unsigned int *bytes) {
- char *ret;
-
- mutex_lock(&cache_lock);
- ret = do_item_cachedump(slabs_clsid, limit, bytes);
- mutex_unlock(&cache_lock);
- return ret;
-}
-
-/*
- * Dumps statistics about slab classes
- */
-void item_stats(ADD_STAT add_stats, void *c) {
- mutex_lock(&cache_lock);
- do_item_stats(add_stats, c);
- mutex_unlock(&cache_lock);
-}
-
-/*
- * Dumps a list of objects of each size in 32-byte increments
- */
-void item_stats_sizes(ADD_STAT add_stats, void *c) {
- mutex_lock(&cache_lock);
- do_item_stats_sizes(add_stats, c);
- mutex_unlock(&cache_lock);
-}
-
-/******************************* GLOBAL STATS ******************************/
-
-void STATS_LOCK() {
- pthread_mutex_lock(&stats_lock);
-}
-
-void STATS_UNLOCK() {
- pthread_mutex_unlock(&stats_lock);
-}
-
-void threadlocal_stats_reset(void) {
- int ii, sid;
- for (ii = 0; ii < settings.num_threads; ++ii) {
- pthread_mutex_lock(&threads[ii].stats.mutex);
-
- threads[ii].stats.get_cmds = 0;
- threads[ii].stats.get_misses = 0;
- threads[ii].stats.touch_cmds = 0;
- threads[ii].stats.touch_misses = 0;
- threads[ii].stats.delete_misses = 0;
- threads[ii].stats.incr_misses = 0;
- threads[ii].stats.decr_misses = 0;
- threads[ii].stats.cas_misses = 0;
- threads[ii].stats.bytes_read = 0;
- threads[ii].stats.bytes_written = 0;
- threads[ii].stats.flush_cmds = 0;
- threads[ii].stats.conn_yields = 0;
- threads[ii].stats.auth_cmds = 0;
- threads[ii].stats.auth_errors = 0;
-
- for(sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- threads[ii].stats.slab_stats[sid].set_cmds = 0;
- threads[ii].stats.slab_stats[sid].get_hits = 0;
- threads[ii].stats.slab_stats[sid].touch_hits = 0;
- threads[ii].stats.slab_stats[sid].delete_hits = 0;
- threads[ii].stats.slab_stats[sid].incr_hits = 0;
- threads[ii].stats.slab_stats[sid].decr_hits = 0;
- threads[ii].stats.slab_stats[sid].cas_hits = 0;
- threads[ii].stats.slab_stats[sid].cas_badval = 0;
- }
-
- pthread_mutex_unlock(&threads[ii].stats.mutex);
- }
-}
-
-void threadlocal_stats_aggregate(struct thread_stats *stats) {
- int ii, sid;
-
- /* The struct has a mutex, but we can safely set the whole thing
- * to zero since it is unused when aggregating. */
- memset(stats, 0, sizeof(*stats));
-
- for (ii = 0; ii < settings.num_threads; ++ii) {
- pthread_mutex_lock(&threads[ii].stats.mutex);
-
- stats->get_cmds += threads[ii].stats.get_cmds;
- stats->get_misses += threads[ii].stats.get_misses;
- stats->touch_cmds += threads[ii].stats.touch_cmds;
- stats->touch_misses += threads[ii].stats.touch_misses;
- stats->delete_misses += threads[ii].stats.delete_misses;
- stats->decr_misses += threads[ii].stats.decr_misses;
- stats->incr_misses += threads[ii].stats.incr_misses;
- stats->cas_misses += threads[ii].stats.cas_misses;
- stats->bytes_read += threads[ii].stats.bytes_read;
- stats->bytes_written += threads[ii].stats.bytes_written;
- stats->flush_cmds += threads[ii].stats.flush_cmds;
- stats->conn_yields += threads[ii].stats.conn_yields;
- stats->auth_cmds += threads[ii].stats.auth_cmds;
- stats->auth_errors += threads[ii].stats.auth_errors;
-
- for (sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- stats->slab_stats[sid].set_cmds +=
- threads[ii].stats.slab_stats[sid].set_cmds;
- stats->slab_stats[sid].get_hits +=
- threads[ii].stats.slab_stats[sid].get_hits;
- stats->slab_stats[sid].touch_hits +=
- threads[ii].stats.slab_stats[sid].touch_hits;
- stats->slab_stats[sid].delete_hits +=
- threads[ii].stats.slab_stats[sid].delete_hits;
- stats->slab_stats[sid].decr_hits +=
- threads[ii].stats.slab_stats[sid].decr_hits;
- stats->slab_stats[sid].incr_hits +=
- threads[ii].stats.slab_stats[sid].incr_hits;
- stats->slab_stats[sid].cas_hits +=
- threads[ii].stats.slab_stats[sid].cas_hits;
- stats->slab_stats[sid].cas_badval +=
- threads[ii].stats.slab_stats[sid].cas_badval;
- }
-
- pthread_mutex_unlock(&threads[ii].stats.mutex);
- }
-}
-
-void slab_stats_aggregate(struct thread_stats *stats, struct slab_stats *out) {
- int sid;
-
- out->set_cmds = 0;
- out->get_hits = 0;
- out->touch_hits = 0;
- out->delete_hits = 0;
- out->incr_hits = 0;
- out->decr_hits = 0;
- out->cas_hits = 0;
- out->cas_badval = 0;
-
- for (sid = 0; sid < MAX_NUMBER_OF_SLAB_CLASSES; sid++) {
- out->set_cmds += stats->slab_stats[sid].set_cmds;
- out->get_hits += stats->slab_stats[sid].get_hits;
- out->touch_hits += stats->slab_stats[sid].touch_hits;
- out->delete_hits += stats->slab_stats[sid].delete_hits;
- out->decr_hits += stats->slab_stats[sid].decr_hits;
- out->incr_hits += stats->slab_stats[sid].incr_hits;
- out->cas_hits += stats->slab_stats[sid].cas_hits;
- out->cas_badval += stats->slab_stats[sid].cas_badval;
- }
-}
-
-/*
- * Initializes the thread subsystem, creating various worker threads.
- *
- * nthreads Number of worker event handler threads to spawn
- * main_base Event base for main thread
- */
-void thread_init(int nthreads, struct event_base *main_base) {
- int i;
- int power;
-
- pthread_mutex_init(&cache_lock, NULL);
- pthread_mutex_init(&stats_lock, NULL);
-
- pthread_mutex_init(&init_lock, NULL);
- pthread_cond_init(&init_cond, NULL);
-
- pthread_mutex_init(&cqi_freelist_lock, NULL);
- cqi_freelist = NULL;
-
- /* Want a wide lock table, but don't waste memory */
- if (nthreads < 3) {
- power = 10;
- } else if (nthreads < 4) {
- power = 11;
- } else if (nthreads < 5) {
- power = 12;
- } else {
- /* 8192 buckets, and central locks don't scale much past 5 threads */
- power = 13;
- }
-
- item_lock_count = ((unsigned long int)1 << (power));
- item_lock_mask = item_lock_count - 1;
-
- item_locks = calloc(item_lock_count, sizeof(pthread_mutex_t));
- if (! item_locks) {
- perror("Can't allocate item locks");
- exit(1);
- }
- for (i = 0; i < item_lock_count; i++) {
- pthread_mutex_init(&item_locks[i], NULL);
- }
-
- threads = calloc(nthreads, sizeof(LIBEVENT_THREAD));
- if (! threads) {
- perror("Can't allocate thread descriptors");
- exit(1);
- }
-
- dispatcher_thread.base = main_base;
- dispatcher_thread.thread_id = pthread_self();
-
- for (i = 0; i < nthreads; i++) {
- int fds[2];
- if (pipe(fds)) {
- perror("Can't create notify pipe");
- exit(1);
- }
-
- threads[i].notify_receive_fd = fds[0];
- threads[i].notify_send_fd = fds[1];
-
- setup_thread(&threads[i]);
- /* Reserve three fds for the libevent base, and two for the pipe */
- stats.reserved_fds += 5;
- }
-
- /* Create threads after we've done all the libevent setup. */
- for (i = 0; i < nthreads; i++) {
- create_worker(worker_libevent, &threads[i]);
- }
-
- /* Wait for all the threads to set themselves up before returning. */
- pthread_mutex_lock(&init_lock);
- while (init_count < nthreads) {
- pthread_cond_wait(&init_cond, &init_lock);
- }
- pthread_mutex_unlock(&init_lock);
-}
-
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sysexits.h>
-
-#include <assert.h>
-
-static int caught = 0;
-
-static void caught_signal(int which)
-{
- caught = which;
-}
-
-static int wait_for_process(pid_t pid)
-{
- int rv = EX_SOFTWARE;
- int stats = 0;
- int i = 0;
- struct sigaction sig_handler;
-
- sig_handler.sa_handler = caught_signal;
- sig_handler.sa_flags = 0;
-
- sigaction(SIGALRM, &sig_handler, NULL);
- sigaction(SIGHUP, &sig_handler, NULL);
- sigaction(SIGINT, &sig_handler, NULL);
- sigaction(SIGTERM, &sig_handler, NULL);
- sigaction(SIGPIPE, &sig_handler, NULL);
-
- /* Loop forever waiting for the process to quit */
- for (i = 0; ;i++) {
- pid_t p = waitpid(pid, &stats, 0);
- if (p == pid) {
- /* child exited. Let's get out of here */
- rv = WIFEXITED(stats) ?
- WEXITSTATUS(stats) :
- (0x80 | WTERMSIG(stats));
- break;
- } else {
- int sig = 0;
- switch (i) {
- case 0:
- /* On the first iteration, pass the signal through */
- sig = caught > 0 ? caught : SIGTERM;
- if (caught == SIGALRM) {
- fprintf(stderr, "Timeout.. killing the process\n");
- }
- break;
- case 1:
- sig = SIGTERM;
- break;
- default:
- sig = SIGKILL;
- break;
- }
- if (kill(pid, sig) < 0) {
- /* Kill failed. Must have lost the process. :/ */
- perror("lost child when trying to kill");
- }
- /* Wait up to 5 seconds for the pid */
- alarm(5);
- }
- }
- return rv;
-}
-
-static int spawn_and_wait(char **argv)
-{
- int rv = EX_SOFTWARE;
- pid_t pid = fork();
-
- switch (pid) {
- case -1:
- perror("fork");
- rv = EX_OSERR;
- break; /* NOTREACHED */
- case 0:
- execvp(argv[0], argv);
- perror("exec");
- rv = EX_SOFTWARE;
- break; /* NOTREACHED */
- default:
- rv = wait_for_process(pid);
- }
- return rv;
-}
-
-int main(int argc, char **argv)
-{
- int naptime = 0;
- assert(argc > 2);
-
- naptime = atoi(argv[1]);
- assert(naptime > 0 && naptime < 1800);
-
- alarm(naptime);
-
- return spawn_and_wait(argv+2);
-}
+++ /dev/null
-#ifndef TRACE_H
-#define TRACE_H
-
-#ifdef ENABLE_DTRACE
-#include "memcached_dtrace.h"
-#else
-#define MEMCACHED_ASSOC_DELETE(arg0, arg1, arg2)
-#define MEMCACHED_ASSOC_DELETE_ENABLED() (0)
-#define MEMCACHED_ASSOC_FIND(arg0, arg1, arg2)
-#define MEMCACHED_ASSOC_FIND_ENABLED() (0)
-#define MEMCACHED_ASSOC_INSERT(arg0, arg1, arg2)
-#define MEMCACHED_ASSOC_INSERT_ENABLED() (0)
-#define MEMCACHED_COMMAND_ADD(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_ADD_ENABLED() (0)
-#define MEMCACHED_COMMAND_APPEND(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_APPEND_ENABLED() (0)
-#define MEMCACHED_COMMAND_CAS(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_CAS_ENABLED() (0)
-#define MEMCACHED_COMMAND_DECR(arg0, arg1, arg2, arg3)
-#define MEMCACHED_COMMAND_DECR_ENABLED() (0)
-#define MEMCACHED_COMMAND_DELETE(arg0, arg1, arg2)
-#define MEMCACHED_COMMAND_DELETE_ENABLED() (0)
-#define MEMCACHED_COMMAND_GET(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_GET_ENABLED() (0)
-#define MEMCACHED_COMMAND_TOUCH(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_TOUCH_ENABLED() (0)
-#define MEMCACHED_COMMAND_INCR(arg0, arg1, arg2, arg3)
-#define MEMCACHED_COMMAND_INCR_ENABLED() (0)
-#define MEMCACHED_COMMAND_PREPEND(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_PREPEND_ENABLED() (0)
-#define MEMCACHED_COMMAND_REPLACE(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_REPLACE_ENABLED() (0)
-#define MEMCACHED_COMMAND_SET(arg0, arg1, arg2, arg3, arg4)
-#define MEMCACHED_COMMAND_SET_ENABLED() (0)
-#define MEMCACHED_CONN_ALLOCATE(arg0)
-#define MEMCACHED_CONN_ALLOCATE_ENABLED() (0)
-#define MEMCACHED_CONN_CREATE(arg0)
-#define MEMCACHED_CONN_CREATE_ENABLED() (0)
-#define MEMCACHED_CONN_DESTROY(arg0)
-#define MEMCACHED_CONN_DESTROY_ENABLED() (0)
-#define MEMCACHED_CONN_DISPATCH(arg0, arg1)
-#define MEMCACHED_CONN_DISPATCH_ENABLED() (0)
-#define MEMCACHED_CONN_RELEASE(arg0)
-#define MEMCACHED_CONN_RELEASE_ENABLED() (0)
-#define MEMCACHED_ITEM_LINK(arg0, arg1, arg2)
-#define MEMCACHED_ITEM_LINK_ENABLED() (0)
-#define MEMCACHED_ITEM_REMOVE(arg0, arg1, arg2)
-#define MEMCACHED_ITEM_REMOVE_ENABLED() (0)
-#define MEMCACHED_ITEM_REPLACE(arg0, arg1, arg2, arg3, arg4, arg5)
-#define MEMCACHED_ITEM_REPLACE_ENABLED() (0)
-#define MEMCACHED_ITEM_UNLINK(arg0, arg1, arg2)
-#define MEMCACHED_ITEM_UNLINK_ENABLED() (0)
-#define MEMCACHED_ITEM_UPDATE(arg0, arg1, arg2)
-#define MEMCACHED_ITEM_UPDATE_ENABLED() (0)
-#define MEMCACHED_PROCESS_COMMAND_END(arg0, arg1, arg2)
-#define MEMCACHED_PROCESS_COMMAND_END_ENABLED() (0)
-#define MEMCACHED_PROCESS_COMMAND_START(arg0, arg1, arg2)
-#define MEMCACHED_PROCESS_COMMAND_START_ENABLED() (0)
-#define MEMCACHED_SLABS_ALLOCATE(arg0, arg1, arg2, arg3)
-#define MEMCACHED_SLABS_ALLOCATE_ENABLED() (0)
-#define MEMCACHED_SLABS_ALLOCATE_FAILED(arg0, arg1)
-#define MEMCACHED_SLABS_ALLOCATE_FAILED_ENABLED() (0)
-#define MEMCACHED_SLABS_FREE(arg0, arg1, arg2)
-#define MEMCACHED_SLABS_FREE_ENABLED() (0)
-#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE(arg0)
-#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_ENABLED() (0)
-#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(arg0)
-#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED_ENABLED() (0)
-#endif
-
-#endif
+++ /dev/null
-#include <config.h>
-
-#include <stdio.h>
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include "memcached.h"
-
-/* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */
-#define xisspace(c) isspace((unsigned char)c)
-
-bool safe_strtoull(const char *str, uint64_t *out) {
- assert(out != NULL);
- errno = 0;
- *out = 0;
- char *endptr;
- unsigned long long ull = strtoull(str, &endptr, 10);
- if ((errno == ERANGE) || (str == endptr)) {
- return false;
- }
-
- if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
- if ((long long) ull < 0) {
- /* only check for negative signs in the uncommon case when
- * the unsigned number is so big that it's negative as a
- * signed number. */
- if (strchr(str, '-') != NULL) {
- return false;
- }
- }
- *out = ull;
- return true;
- }
- return false;
-}
-
-bool safe_strtoll(const char *str, int64_t *out) {
- assert(out != NULL);
- errno = 0;
- *out = 0;
- char *endptr;
- long long ll = strtoll(str, &endptr, 10);
- if ((errno == ERANGE) || (str == endptr)) {
- return false;
- }
-
- if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
- *out = ll;
- return true;
- }
- return false;
-}
-
-bool safe_strtoul(const char *str, uint32_t *out) {
- char *endptr = NULL;
- unsigned long l = 0;
- assert(out);
- assert(str);
- *out = 0;
- errno = 0;
-
- l = strtoul(str, &endptr, 10);
- if ((errno == ERANGE) || (str == endptr)) {
- return false;
- }
-
- if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
- if ((long) l < 0) {
- /* only check for negative signs in the uncommon case when
- * the unsigned number is so big that it's negative as a
- * signed number. */
- if (strchr(str, '-') != NULL) {
- return false;
- }
- }
- *out = l;
- return true;
- }
-
- return false;
-}
-
-bool safe_strtol(const char *str, int32_t *out) {
- assert(out != NULL);
- errno = 0;
- *out = 0;
- char *endptr;
- long l = strtol(str, &endptr, 10);
- if ((errno == ERANGE) || (str == endptr)) {
- return false;
- }
-
- if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
- *out = l;
- return true;
- }
- return false;
-}
-
-void vperror(const char *fmt, ...) {
- int old_errno = errno;
- char buf[1024];
- va_list ap;
-
- va_start(ap, fmt);
- if (vsnprintf(buf, sizeof(buf), fmt, ap) == -1) {
- buf[sizeof(buf) - 1] = '\0';
- }
- va_end(ap);
-
- errno = old_errno;
-
- perror(buf);
-}
-
-#ifndef HAVE_HTONLL
-static uint64_t mc_swap64(uint64_t in) {
-#ifdef ENDIAN_LITTLE
- /* Little endian, flip the bytes around until someone makes a faster/better
- * way to do this. */
- int64_t rv = 0;
- int i = 0;
- for(i = 0; i<8; i++) {
- rv = (rv << 8) | (in & 0xff);
- in >>= 8;
- }
- return rv;
-#else
- /* big-endian machines don't need byte swapping */
- return in;
-#endif
-}
-
-uint64_t ntohll(uint64_t val) {
- return mc_swap64(val);
-}
-
-uint64_t htonll(uint64_t val) {
- return mc_swap64(val);
-}
-#endif
-
+++ /dev/null
-/*
- * Wrappers around strtoull/strtoll that are safer and easier to
- * use. For tests and assumptions, see internal_tests.c.
- *
- * str a NULL-terminated base decimal 10 unsigned integer
- * out out parameter, if conversion succeeded
- *
- * returns true if conversion succeeded.
- */
-bool safe_strtoull(const char *str, uint64_t *out);
-bool safe_strtoll(const char *str, int64_t *out);
-bool safe_strtoul(const char *str, uint32_t *out);
-bool safe_strtol(const char *str, int32_t *out);
-
-#ifndef HAVE_HTONLL
-extern uint64_t htonll(uint64_t);
-extern uint64_t ntohll(uint64_t);
-#endif
-
-#ifdef __GCC
-# define __gcc_attribute__ __attribute__
-#else
-# define __gcc_attribute__(x)
-#endif
-
-/**
- * Vararg variant of perror that makes for more useful error messages
- * when reporting with parameters.
- *
- * @param fmt a printf format
- */
-void vperror(const char *fmt, ...)
- __gcc_attribute__ ((format (printf, 1, 2)));
+++ /dev/null
-#!/usr/bin/perl
-# If you think this is stupid/overkill, blame dormando
-
-use warnings;
-use strict;
-
-my $version = `git describe`;
-chomp $version;
-# Test the various versions.
-#my $version = 'foob';
-#my $version = '1.4.2-30-gf966dba';
-#my $version = '1.4.3-rc1';
-#my $version = '1.4.3';
-unless ($version =~ m/^\d+\.\d+\.\d+/) {
- write_file('version.m4', "m4_define([VERSION_NUMBER], [UNKNOWN])\n");
- exit;
-}
-
-$version =~ s/-/_/g;
-write_file('version.m4', "m4_define([VERSION_NUMBER], [$version])\n");
-my ($VERSION, $FULLVERSION, $RELEASE);
-
-if ($version =~ m/^(\d+\.\d+\.\d+)_rc(\d+)$/) {
- $VERSION = $1;
- $FULLVERSION = $version;
- $RELEASE = '0.1.rc' . $2;
-} elsif ($version =~ m/^(\d+\.\d+\.\d+)_(.+)$/) {
- $VERSION = $1;
- $FULLVERSION = $version;
- $RELEASE = '1.' . $2;
-} elsif ($version =~ m/^(\d+\.\d+\.\d+)$/) {
- $VERSION = $1;
- $FULLVERSION = $version;
- $RELEASE = '1';
-}
-
-my $spec = read_file('memcached.spec.in');
-$spec =~ s/\@VERSION\@/$VERSION/gm;
-$spec =~ s/\@FULLVERSION\@/$FULLVERSION/gm;
-$spec =~ s/\@RELEASE\@/$RELEASE/gm;
-
-write_file('memcached.spec', $spec);
-
-sub write_file {
- my $file = shift;
- my $data = shift;
- open(my $fh, "> $file") or die "Can't open $file: $!";
- print $fh $data;
- close($fh);
-}
-
-sub read_file {
- my $file = shift;
- local $/ = undef;
- open(my $fh, "< $file") or die "Can't open $file: $!";
- my $data = <$fh>;
- close($fh);
- return $data;
-}
+++ /dev/null
-#!/bin/sh
-
-if git describe | sed s/-/_/g > version.num.tmp
-then
- mv version.num.tmp version.num
- echo "m4_define([VERSION_NUMBER], [`tr -d '\n' < version.num`])" \
- > version.m4
- sed s/@VERSION@/`cat version.num`/ < memcached.spec.in > memcached.spec
-else
- rm version.num.tmp
-fi