Index: tags/tug_0_1/tugelacache/items.c |
— | — | @@ -0,0 +1,126 @@ |
| 2 | +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 3 | +/* $Id$ */ |
| 4 | + |
| 5 | +#include <sys/types.h> |
| 6 | +#include <sys/stat.h> |
| 7 | +#include <sys/time.h> |
| 8 | +#include <sys/socket.h> |
| 9 | +#include <sys/signal.h> |
| 10 | +#include <sys/resource.h> |
| 11 | +#include <fcntl.h> |
| 12 | +#include <stdlib.h> |
| 13 | +#include <stdio.h> |
| 14 | +#include <string.h> |
| 15 | +#include <unistd.h> |
| 16 | +#include <netinet/in.h> |
| 17 | +#include <errno.h> |
| 18 | +#include <time.h> |
| 19 | +#include <event.h> |
| 20 | +#include <assert.h> |
| 21 | + |
| 22 | +#include "dbcached.h" |
| 23 | + |
| 24 | + |
| 25 | +/* |
| 26 | + * NOTE: we assume here for simplicity that slab ids are <=32. That's true in |
| 27 | + * the powers-of-2 implementation, but if that changes this should be changed too |
| 28 | + */ |
| 29 | + |
| 30 | +#define LARGEST_ID 32 |
| 31 | +static item *heads[LARGEST_ID]; |
| 32 | +static item *tails[LARGEST_ID]; |
| 33 | +unsigned int sizes[LARGEST_ID]; |
| 34 | + |
| 35 | +void item_init(void) |
| 36 | +{ |
| 37 | + int i; |
| 38 | + for (i = 0; i < LARGEST_ID; i++) { |
| 39 | + heads[i] = 0; |
| 40 | + tails[i] = 0; |
| 41 | + sizes[i] = 0; |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | + |
| 46 | +item *item_alloc(char *key, int flags, time_t exptime, int nbytes) |
| 47 | +{ |
| 48 | + int ntotal, len; |
| 49 | + item *it = NULL; |
| 50 | + |
| 51 | + len = strlen(key) + 1; |
| 52 | + if (len % 4) |
| 53 | + len += 4 - (len % 4); |
| 54 | + ntotal = sizeof(item) + len + nbytes; |
| 55 | + |
| 56 | + it = realloc(it, ntotal); |
| 57 | + it->refcount = 0; |
| 58 | + it->it_flags = 0; |
| 59 | + it->nkey = len; |
| 60 | + it->nbytes = nbytes; |
| 61 | + strcpy(ITEM_key(it), key); |
| 62 | + it->exptime = exptime; |
| 63 | + it->flags = flags; |
| 64 | + it->time = time(0); |
| 65 | + return it; |
| 66 | +} |
| 67 | + |
| 68 | +void item_free(item * it) |
| 69 | +{ |
| 70 | + assert((it->it_flags & ITEM_LINKED) == 0); |
| 71 | + assert(it->refcount == 0); |
| 72 | + |
| 73 | + /* so slab size changer can tell later if item is already free or not */ |
| 74 | + it->it_flags |= ITEM_SLABBED; |
| 75 | +} |
| 76 | + |
| 77 | +void item_unlink(item * it) |
| 78 | +{ |
| 79 | + free(it); |
| 80 | +} |
| 81 | + |
| 82 | +void item_remove(item * it) |
| 83 | +{ |
| 84 | + free(it); |
| 85 | +} |
| 86 | + |
| 87 | +void item_update(item * it) |
| 88 | +{ |
| 89 | + it->time = time(0); |
| 90 | +} |
| 91 | + |
| 92 | +int item_replace(item * it, item * new_it) |
| 93 | +{ |
| 94 | + return 0; |
| 95 | +} |
| 96 | + |
| 97 | +char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, |
| 98 | + unsigned int *bytes) |
| 99 | +{ |
| 100 | + |
| 101 | + int memlimit = 2 * 1024 * 1024; |
| 102 | + char *buffer; |
| 103 | + int bufcurr; |
| 104 | + |
| 105 | + buffer = malloc(memlimit); |
| 106 | + if (buffer == 0) |
| 107 | + return 0; |
| 108 | + bufcurr = 0; |
| 109 | + strcpy(buffer + bufcurr, "END\r\n"); |
| 110 | + bufcurr += 5; |
| 111 | + |
| 112 | + *bytes = bufcurr; |
| 113 | + return buffer; |
| 114 | +} |
| 115 | + |
| 116 | +void item_stats(char *buffer, int buflen) |
| 117 | +{ |
| 118 | + char *bufcurr = buffer; |
| 119 | + |
| 120 | + if (buflen < 4096) { |
| 121 | + strcpy(buffer, "SERVER_ERROR out of memory"); |
| 122 | + return; |
| 123 | + } |
| 124 | + |
| 125 | + strcpy(bufcurr, "END"); |
| 126 | + return; |
| 127 | +} |
Property changes on: tags/tug_0_1/tugelacache/items.c |
___________________________________________________________________ |
Added: svn:keywords |
1 | 128 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 129 | + native |
Index: tags/tug_0_1/tugelacache/dbcached.h |
— | — | @@ -0,0 +1,213 @@ |
| 2 | +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 3 | +/* $Id$ */ |
| 4 | + |
| 5 | +#define DATA_BUFFER_SIZE 2048 |
| 6 | + |
| 7 | +#if defined(TCP_CORK) && !defined(TCP_NOPUSH) |
| 8 | +#define TCP_NOPUSH TCP_CORK |
| 9 | +#endif |
| 10 | + |
| 11 | +struct stats { |
| 12 | + unsigned int curr_items; |
| 13 | + unsigned int total_items; |
| 14 | + unsigned long long curr_bytes; |
| 15 | + unsigned int curr_conns; |
| 16 | + unsigned int total_conns; |
| 17 | + unsigned int conn_structs; |
| 18 | + unsigned int get_cmds; |
| 19 | + unsigned int set_cmds; |
| 20 | + unsigned int get_hits; |
| 21 | + unsigned int get_misses; |
| 22 | + time_t started; /* when the process was started */ |
| 23 | + unsigned long long bytes_read; |
| 24 | + unsigned long long bytes_written; |
| 25 | +}; |
| 26 | + |
| 27 | +struct settings { |
| 28 | + unsigned int maxbytes; |
| 29 | + int maxconns; |
| 30 | + int synctimer; |
| 31 | + int port; |
| 32 | + struct in_addr interface; |
| 33 | + int verbose; |
| 34 | + time_t oldest_live; /* ignore existing items older than this */ |
| 35 | + int evict_to_free; |
| 36 | +}; |
| 37 | + |
| 38 | +extern struct stats stats; |
| 39 | +extern struct settings settings; |
| 40 | + |
| 41 | +#define ITEM_LINKED 1 |
| 42 | +#define ITEM_DELETED 2 |
| 43 | + |
| 44 | +/* temp */ |
| 45 | +#define ITEM_SLABBED 4 |
| 46 | + |
| 47 | +typedef struct _stritem { |
| 48 | + unsigned short refcount; |
| 49 | + unsigned short flags; |
| 50 | + int nbytes; /* size of data */ |
| 51 | + time_t time; /* least recent access */ |
| 52 | + time_t exptime; /* expire time */ |
| 53 | + unsigned char it_flags; /* ITEM_* above */ |
| 54 | + unsigned char nkey; /* key length, with terminating null and padding */ |
| 55 | + unsigned char dummy1; |
| 56 | + void *end[0]; |
| 57 | +} item; |
| 58 | + |
| 59 | +#define ITEM_key(item) ((char*)&((item)->end[0])) |
| 60 | + |
| 61 | +/* warning: don't use these macros with a function, as it evals its arg twice */ |
| 62 | +#define ITEM_data(item) ((char*) &((item)->end[0]) + (item)->nkey) |
| 63 | +#define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + (item)->nbytes) |
| 64 | + |
| 65 | +enum conn_states { |
| 66 | + conn_listening, /* the socket which listens for connections */ |
| 67 | + conn_read, /* reading in a command line */ |
| 68 | + conn_write, /* writing out a simple response */ |
| 69 | + conn_nread, /* reading in a fixed number of bytes */ |
| 70 | + conn_swallow, /* swallowing unnecessary bytes w/o storing */ |
| 71 | + conn_closing, /* closing this connection */ |
| 72 | + conn_mwrite /* writing out many items sequentially */ |
| 73 | +}; |
| 74 | + |
| 75 | +#define NREAD_ADD 1 |
| 76 | +#define NREAD_SET 2 |
| 77 | +#define NREAD_REPLACE 3 |
| 78 | + |
| 79 | +typedef struct { |
| 80 | + int sfd; |
| 81 | + int state; |
| 82 | + struct event event; |
| 83 | + short ev_flags; |
| 84 | + short which; /* which events were just triggered */ |
| 85 | + |
| 86 | + char *rbuf; |
| 87 | + int rsize; |
| 88 | + int rbytes; |
| 89 | + |
| 90 | + char *wbuf; |
| 91 | + char *wcurr; |
| 92 | + int wsize; |
| 93 | + int wbytes; |
| 94 | + int write_and_go; /* which state to go into after finishing current write */ |
| 95 | + void *write_and_free; /* free this memory after finishing writing */ |
| 96 | + char is_corked; /* boolean, connection is corked */ |
| 97 | + |
| 98 | + char *rcurr; |
| 99 | + int rlbytes; |
| 100 | + |
| 101 | + /* data for the nread state */ |
| 102 | + |
| 103 | + /* |
| 104 | + * item is used to hold an item structure created after reading the command |
| 105 | + * line of set/add/replace commands, but before we finished reading the actual |
| 106 | + * data. The data is read into ITEM_data(item) to avoid extra copying. |
| 107 | + */ |
| 108 | + |
| 109 | + void *item; /* for commands set/add/replace */ |
| 110 | + int item_comm; /* which one is it: set/add/replace */ |
| 111 | + |
| 112 | + /* data for the swallow state */ |
| 113 | + int sbytes; /* how many bytes to swallow */ |
| 114 | + |
| 115 | + /* data for the mwrite state */ |
| 116 | + item **ilist; /* list of items to write out */ |
| 117 | + int isize; |
| 118 | + item **icurr; |
| 119 | + int ileft; |
| 120 | + int ipart; /* 1 if we're writing a VALUE line, 2 if we're writing data */ |
| 121 | + char ibuf[300]; /* for VALUE lines */ |
| 122 | + char *iptr; |
| 123 | + int ibytes; |
| 124 | + |
| 125 | +} conn; |
| 126 | + |
| 127 | +/* listening socket */ |
| 128 | +extern int l_socket; |
| 129 | + |
| 130 | +/* temporary hack */ |
| 131 | +/* #define assert(x) if(!(x)) { printf("assert failure: %s\n", #x); pre_gdb(); } |
| 132 | + void pre_gdb (); */ |
| 133 | + |
| 134 | +/* |
| 135 | + * Functions |
| 136 | + */ |
| 137 | + |
| 138 | +/* |
| 139 | + * given time value that's either unix time or delta from current unix time, return |
| 140 | + * unix time. Use the fact that delta can't exceed one month (and real time value can't |
| 141 | + * be that low). |
| 142 | + */ |
| 143 | + |
| 144 | +time_t realtime(time_t exptime); |
| 145 | + |
| 146 | +/* slabs memory allocation */ |
| 147 | + |
| 148 | +/* Init the subsystem. The argument is the limit on no. of bytes to allocate, 0 if no limit */ |
| 149 | +void slabs_init(unsigned int limit); |
| 150 | + |
| 151 | +/* Given object size, return id to use when allocating/freeing memory for object */ |
| 152 | +/* 0 means error: can't store such a large object */ |
| 153 | +unsigned int slabs_clsid(unsigned int size); |
| 154 | + |
| 155 | +/* Allocate object of given length. 0 on error */ |
| 156 | +void *slabs_alloc(unsigned int size); |
| 157 | + |
| 158 | +/* Free previously allocated object */ |
| 159 | +void slabs_free(void *ptr, unsigned int size); |
| 160 | + |
| 161 | +/* Fill buffer with stats */ |
| 162 | +char *slabs_stats(int *buflen); |
| 163 | + |
| 164 | +/* Request some slab be moved between classes |
| 165 | + 1 = success |
| 166 | + 0 = fail |
| 167 | + -1 = tried. busy. send again shortly. */ |
| 168 | +int slabs_reassign(unsigned char srcid, unsigned char dstid); |
| 169 | + |
| 170 | +/* event handling, network IO */ |
| 171 | +void event_handler(int fd, short which, void *arg); |
| 172 | +conn *conn_new(int sfd, int init_state, int event_flags); |
| 173 | +void conn_close(conn * c); |
| 174 | +void conn_init(void); |
| 175 | +void drive_machine(conn * c); |
| 176 | +int new_socket(void); |
| 177 | +int server_socket(int port); |
| 178 | +int update_event(conn * c, int new_flags); |
| 179 | +int try_read_command(conn * c); |
| 180 | +int try_read_network(conn * c); |
| 181 | +void complete_nread(conn * c); |
| 182 | +void process_command(conn * c, char *command); |
| 183 | + |
| 184 | +/* stats */ |
| 185 | +void stats_reset(void); |
| 186 | +void stats_init(void); |
| 187 | + |
| 188 | +/* defaults */ |
| 189 | +void settings_init(void); |
| 190 | + |
| 191 | +/* associative array */ |
| 192 | +void assoc_init(void); |
| 193 | +item *assoc_find(char *key); |
| 194 | +int assoc_insert(char *key, item * item); |
| 195 | +void assoc_delete(char *key); |
| 196 | + |
| 197 | + |
| 198 | +void item_init(void); |
| 199 | +item *item_alloc(char *key, int flags, time_t exptime, int nbytes); |
| 200 | +void item_free(item * it); |
| 201 | + |
| 202 | +int item_link(item * it); /* may fail if transgresses limits */ |
| 203 | +void item_unlink(item * it); |
| 204 | +void item_remove(item * it); |
| 205 | +void cleanup_dbt(void); |
| 206 | + |
| 207 | +void item_update(item * it); /* update LRU time to current and reposition */ |
| 208 | +int item_replace(item * it, item * new_it); |
| 209 | +char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, |
| 210 | + unsigned int *bytes); |
| 211 | +char *item_stats_sizes(int *bytes); |
| 212 | +void item_stats(char *buffer, int buflen); |
| 213 | + |
| 214 | +void syncdb(void); |
Property changes on: tags/tug_0_1/tugelacache/dbcached.h |
___________________________________________________________________ |
Added: svn:keywords |
1 | 215 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 216 | + native |
Index: tags/tug_0_1/tugelacache/AUTHORS |
— | — | @@ -0,0 +1,3 @@ |
| 2 | +Anatoly Vorobey <mellon@pobox.com> |
| 3 | +Brad Fitzpatrick <brad@danga.com> |
| 4 | +Domas Mituzas <domas.mituzas@gmail.com> |
Property changes on: tags/tug_0_1/tugelacache/AUTHORS |
___________________________________________________________________ |
Added: svn:keywords |
1 | 5 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 6 | + native |
Index: tags/tug_0_1/tugelacache/.deps/dbcached.pp |
— | — | @@ -0,0 +1,17 @@ |
| 2 | +dbcached.o: dbcached.c config.h /usr/include/sys/types.h \ |
| 3 | + /usr/include/sys/cdefs.h /usr/include/sys/inttypes.h \ |
| 4 | + /usr/include/machine/ansi.h /usr/include/machine/types.h \ |
| 5 | + /usr/include/machine/endian.h /usr/include/sys/stat.h \ |
| 6 | + /usr/include/sys/time.h /usr/include/time.h /usr/include/sys/_posix.h \ |
| 7 | + /usr/include/sys/socket.h /usr/include/machine/param.h \ |
| 8 | + /usr/include/sys/signal.h /usr/include/machine/signal.h \ |
| 9 | + /usr/include/machine/trap.h /usr/include/sys/ucontext.h \ |
| 10 | + /usr/include/machine/ucontext.h /usr/include/sys/resource.h \ |
| 11 | + /usr/include/pwd.h /usr/include/sys/mman.h /usr/include/fcntl.h \ |
| 12 | + /usr/include/stdlib.h /usr/include/stdio.h /usr/include/string.h \ |
| 13 | + /usr/include/unistd.h /usr/include/sys/unistd.h \ |
| 14 | + /usr/include/netinet/in.h /usr/include/netinet6/in6.h \ |
| 15 | + /usr/include/netinet/tcp.h /usr/include/arpa/inet.h \ |
| 16 | + /usr/include/errno.h /usr/local/include/event.h /usr/include/assert.h \ |
| 17 | + /usr/include/db.h /usr/include/limits.h /usr/include/machine/limits.h \ |
| 18 | + /usr/include/sys/syslimits.h /usr/include/malloc.h dbcached.h |
Property changes on: tags/tug_0_1/tugelacache/.deps/dbcached.pp |
___________________________________________________________________ |
Added: svn:keywords |
1 | 19 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 20 | + native |
Index: tags/tug_0_1/tugelacache/assoc.c |
— | — | @@ -0,0 +1,168 @@ |
| 2 | +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 3 | +/* |
| 4 | + * Hash table |
| 5 | + * |
| 6 | + * The hash function used here is by Bob Jenkins, 1996: |
| 7 | + * <http://burtleburtle.net/bob/hash/doobs.html> |
| 8 | + * "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. |
| 9 | + * You may use this code any way you wish, private, educational, |
| 10 | + * or commercial. It's free." |
| 11 | + * |
| 12 | + * The rest of the file is licensed under the BSD license. See LICENSE. |
| 13 | + * |
| 14 | + * $Id$ |
| 15 | + */ |
| 16 | + |
| 17 | +#include <sys/types.h> |
| 18 | +#include <sys/stat.h> |
| 19 | +#include <sys/time.h> |
| 20 | +#include <sys/socket.h> |
| 21 | +#include <sys/signal.h> |
| 22 | +#include <sys/resource.h> |
| 23 | +#include <fcntl.h> |
| 24 | +#include <stdlib.h> |
| 25 | +#include <stdio.h> |
| 26 | +#include <string.h> |
| 27 | +#include <unistd.h> |
| 28 | +#include <netinet/in.h> |
| 29 | +#include <errno.h> |
| 30 | +#include <event.h> |
| 31 | +#include <assert.h> |
| 32 | + |
| 33 | +#include "dbcached.h" |
| 34 | + |
| 35 | +typedef unsigned long int ub4; /* unsigned 4-byte quantities */ |
| 36 | +typedef unsigned char ub1; /* unsigned 1-byte quantities */ |
| 37 | + |
| 38 | +/* hard-code one million buckets, for now (2**20 == 4MB hash) */ |
| 39 | +#define HASHPOWER 20 |
| 40 | + |
| 41 | +#define hashsize(n) ((ub4)1<<(n)) |
| 42 | +#define hashmask(n) (hashsize(n)-1) |
| 43 | + |
| 44 | +#define mix(a,b,c) \ |
| 45 | +{ \ |
| 46 | + a -= b; a -= c; a ^= (c>>13); \ |
| 47 | + b -= c; b -= a; b ^= (a<<8); \ |
| 48 | + c -= a; c -= b; c ^= (b>>13); \ |
| 49 | + a -= b; a -= c; a ^= (c>>12); \ |
| 50 | + b -= c; b -= a; b ^= (a<<16); \ |
| 51 | + c -= a; c -= b; c ^= (b>>5); \ |
| 52 | + a -= b; a -= c; a ^= (c>>3); \ |
| 53 | + b -= c; b -= a; b ^= (a<<10); \ |
| 54 | + c -= a; c -= b; c ^= (b>>15); \ |
| 55 | +} |
| 56 | + |
| 57 | +/* |
| 58 | +-------------------------------------------------------------------- |
| 59 | +hash() -- hash a variable-length key into a 32-bit value |
| 60 | + k : the key (the unaligned variable-length array of bytes) |
| 61 | + len : the length of the key, counting by bytes |
| 62 | + initval : can be any 4-byte value |
| 63 | +Returns a 32-bit value. Every bit of the key affects every bit of |
| 64 | +the return value. Every 1-bit and 2-bit delta achieves avalanche. |
| 65 | +About 6*len+35 instructions. |
| 66 | + |
| 67 | +The best hash table sizes are powers of 2. There is no need to do |
| 68 | +mod a prime (mod is sooo slow!). If you need less than 32 bits, |
| 69 | +use a bitmask. For example, if you need only 10 bits, do |
| 70 | + h = (h & hashmask(10)); |
| 71 | +In which case, the hash table should have hashsize(10) elements. |
| 72 | + |
| 73 | +If you are hashing n strings (ub1 **)k, do it like this: |
| 74 | + for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h); |
| 75 | + |
| 76 | +By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this |
| 77 | +code any way you wish, private, educational, or commercial. It's free. |
| 78 | + |
| 79 | +See http://burtleburtle.net/bob/hash/evahash.html |
| 80 | +Use for hash table lookup, or anything where one collision in 2^^32 is |
| 81 | +acceptable. Do NOT use for cryptographic purposes. |
| 82 | +-------------------------------------------------------------------- |
| 83 | +*/ |
| 84 | + |
| 85 | +ub4 hash(k, length, initval) |
| 86 | +register ub1 *k; /* the key */ |
| 87 | +register ub4 length; /* the length of the key */ |
| 88 | +register ub4 initval; /* the previous hash, or an arbitrary value */ |
| 89 | +{ |
| 90 | + register ub4 a, b, c, len; |
| 91 | + |
| 92 | + /* Set up the internal state */ |
| 93 | + len = length; |
| 94 | + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ |
| 95 | + c = initval; /* the previous hash value */ |
| 96 | + |
| 97 | + /*---------------------------------------- handle most of the key */ |
| 98 | + while (len >= 12) { |
| 99 | + a += (k[0] + ((ub4) k[1] << 8) + ((ub4) k[2] << 16) + |
| 100 | + ((ub4) k[3] << 24)); |
| 101 | + b += (k[4] + ((ub4) k[5] << 8) + ((ub4) k[6] << 16) + |
| 102 | + ((ub4) k[7] << 24)); |
| 103 | + c += (k[8] + ((ub4) k[9] << 8) + ((ub4) k[10] << 16) + |
| 104 | + ((ub4) k[11] << 24)); |
| 105 | + mix(a, b, c); |
| 106 | + k += 12; |
| 107 | + len -= 12; |
| 108 | + } |
| 109 | + |
| 110 | + /*------------------------------------- handle the last 11 bytes */ |
| 111 | + c += length; |
| 112 | + switch (len) { /* all the case statements fall through */ |
| 113 | + case 11: |
| 114 | + c += ((ub4) k[10] << 24); |
| 115 | + case 10: |
| 116 | + c += ((ub4) k[9] << 16); |
| 117 | + case 9: |
| 118 | + c += ((ub4) k[8] << 8); |
| 119 | + /* the first byte of c is reserved for the length */ |
| 120 | + case 8: |
| 121 | + b += ((ub4) k[7] << 24); |
| 122 | + case 7: |
| 123 | + b += ((ub4) k[6] << 16); |
| 124 | + case 6: |
| 125 | + b += ((ub4) k[5] << 8); |
| 126 | + case 5: |
| 127 | + b += k[4]; |
| 128 | + case 4: |
| 129 | + a += ((ub4) k[3] << 24); |
| 130 | + case 3: |
| 131 | + a += ((ub4) k[2] << 16); |
| 132 | + case 2: |
| 133 | + a += ((ub4) k[1] << 8); |
| 134 | + case 1: |
| 135 | + a += k[0]; |
| 136 | + /* case 0: nothing left to add */ |
| 137 | + } |
| 138 | + mix(a, b, c); |
| 139 | + /*-------------------------------------------- report the result */ |
| 140 | + return c; |
| 141 | +} |
| 142 | + |
| 143 | +static item **hashtable = 0; |
| 144 | + |
| 145 | +void assoc_init(void) |
| 146 | +{ |
| 147 | + unsigned int hash_size = hashsize(HASHPOWER) * sizeof(void *); |
| 148 | + hashtable = malloc(hash_size); |
| 149 | + if (!hashtable) { |
| 150 | + fprintf(stderr, "Failed to init hashtable.\n"); |
| 151 | + exit(1); |
| 152 | + } |
| 153 | + memset(hashtable, 0, hash_size); |
| 154 | +} |
| 155 | + |
| 156 | +item *assoc_find(char *key) |
| 157 | +{ |
| 158 | + return 0; |
| 159 | +} |
| 160 | + |
| 161 | +/* Note: this isn't an assoc_update. The key must not already exist to call this */ |
| 162 | +int assoc_insert(char *key, item * it) |
| 163 | +{ |
| 164 | + return 0; |
| 165 | +} |
| 166 | + |
| 167 | +void assoc_delete(char *key) |
| 168 | +{ |
| 169 | +} |
Property changes on: tags/tug_0_1/tugelacache/assoc.c |
___________________________________________________________________ |
Added: svn:keywords |
1 | 170 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 171 | + native |
Index: tags/tug_0_1/tugelacache/config.h |
— | — | @@ -0,0 +1,68 @@ |
| 2 | +/* default path of database */ |
| 3 | +/* #undef DBFILE */ |
| 4 | + |
| 5 | +/* Define to 1 if you have the <inttypes.h> header file. */ |
| 6 | +#define HAVE_INTTYPES_H 1 |
| 7 | + |
| 8 | +/* Define to 1 if you have the `db41' library (-ldb41). */ |
| 9 | +/* #undef HAVE_LIBDB41 */ |
| 10 | + |
| 11 | +/* Define to 1 if you have the `event' library (-levent). */ |
| 12 | +#define HAVE_LIBEVENT 1 |
| 13 | + |
| 14 | +/* Define to 1 if you have the <memory.h> header file. */ |
| 15 | +#define HAVE_MEMORY_H 1 |
| 16 | + |
| 17 | +/* Define to 1 if you have the `mlockall' function. */ |
| 18 | +/* #undef HAVE_MLOCKALL */ |
| 19 | + |
| 20 | +/* Define to 1 if you have the <stdint.h> header file. */ |
| 21 | +/* #undef HAVE_STDINT_H */ |
| 22 | + |
| 23 | +/* Define to 1 if you have the <stdlib.h> header file. */ |
| 24 | +#define HAVE_STDLIB_H 1 |
| 25 | + |
| 26 | +/* Define to 1 if you have the <strings.h> header file. */ |
| 27 | +#define HAVE_STRINGS_H 1 |
| 28 | + |
| 29 | +/* Define to 1 if you have the <string.h> header file. */ |
| 30 | +#define HAVE_STRING_H 1 |
| 31 | + |
| 32 | +/* do we have stuct mallinfo? */ |
| 33 | +/* #undef HAVE_STRUCT_MALLINFO */ |
| 34 | + |
| 35 | +/* Define to 1 if you have the <sys/stat.h> header file. */ |
| 36 | +#define HAVE_SYS_STAT_H 1 |
| 37 | + |
| 38 | +/* Define to 1 if you have the <sys/types.h> header file. */ |
| 39 | +#define HAVE_SYS_TYPES_H 1 |
| 40 | + |
| 41 | +/* Define to 1 if you have the <unistd.h> header file. */ |
| 42 | +#define HAVE_UNISTD_H 1 |
| 43 | + |
| 44 | +/* Name of package */ |
| 45 | +#define PACKAGE "tugela" |
| 46 | + |
| 47 | +/* Define to the address where bug reports for this package should be sent. */ |
| 48 | +#define PACKAGE_BUGREPORT "domas.mituzas@gmail.com" |
| 49 | + |
| 50 | +/* Define to the full name of this package. */ |
| 51 | +#define PACKAGE_NAME "tugela object store daemon" |
| 52 | + |
| 53 | +/* Define to the full name and version of this package. */ |
| 54 | +#define PACKAGE_STRING "tugela 0.1" |
| 55 | + |
| 56 | +/* Define to the one symbol short name of this package. */ |
| 57 | +#define PACKAGE_TARNAME "tugela" |
| 58 | + |
| 59 | +/* Define to the version of this package. */ |
| 60 | +#define PACKAGE_VERSION "0.1" |
| 61 | + |
| 62 | +/* Define to 1 if you have the ANSI C header files. */ |
| 63 | +#define STDC_HEADERS 1 |
| 64 | + |
| 65 | +/* Version number of package */ |
| 66 | +#define VERSION "0.1" |
| 67 | + |
| 68 | +/* define to int if socklen_t not available */ |
| 69 | +/* #undef socklen_t */ |
Property changes on: tags/tug_0_1/tugelacache/config.h |
___________________________________________________________________ |
Added: svn:keywords |
1 | 70 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 71 | + native |
Index: tags/tug_0_1/tugelacache/NEWS |
Property changes on: tags/tug_0_1/tugelacache/NEWS |
___________________________________________________________________ |
Added: svn:keywords |
3 | 72 | + Author Date Id Revision |
Added: svn:eol-style |
4 | 73 | + native |
Index: tags/tug_0_1/tugelacache/.cvsignore |
— | — | @@ -0,0 +1,5 @@ |
| 2 | +*~ |
| 3 | +*.o |
| 4 | +tugela |
| 5 | +dbcache |
| 6 | +tugela-expire |
Property changes on: tags/tug_0_1/tugelacache/.cvsignore |
___________________________________________________________________ |
Added: svn:keywords |
1 | 7 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 8 | + native |
Index: tags/tug_0_1/tugelacache/Makefile |
— | — | @@ -0,0 +1,28 @@ |
| 2 | +# $Id$ |
| 3 | +# |
| 4 | +# It is not a real makefile, but sometimes it does work, and it is a place |
| 5 | +# where you should change one thing or another :) |
| 6 | + |
| 7 | +# Default path for a database |
| 8 | +DBFILE='"/var/db/dbcache"' |
| 9 | + |
| 10 | +# change it into your installed db3/db4{0,1,2} library name |
| 11 | +DBLIB=db41 |
| 12 | + |
| 13 | +# FreeBSD file locations |
| 14 | +LIBS=-l${DBLIB} -levent |
| 15 | +DBLIBPATH=/usr/local/lib/${DBLIB}/ |
| 16 | +DBINCPATH=/usr/local/include/${DBLIB}/ |
| 17 | + |
| 18 | +LDFLAGS= -L/usr/local/lib/ -L${DBLIBPATH} |
| 19 | +CFLAGS = -O -pipe -I/usr/local/include -I${DBINCPATH} -DDBFILE=${DBFILE} |
| 20 | + |
| 21 | +all: tugela tugela-expire |
| 22 | + |
| 23 | +tugela: dbcached.o items.o assoc.o |
| 24 | + cc -o tugela ${LDFLAGS} dbcached.o items.o assoc.o ${LIBS} |
| 25 | + |
| 26 | +tugela-expire: expire.c |
| 27 | + cc -o tugela-expire ${LDFLAGS} ${LIBS} ${CFLAGS} expire.c |
| 28 | +clean: |
| 29 | + rm -f -- *.o tugela tugela-expire *~ *.bak *core |
Property changes on: tags/tug_0_1/tugelacache/Makefile |
___________________________________________________________________ |
Added: svn:keywords |
1 | 30 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 31 | + native |
Index: tags/tug_0_1/tugelacache/README |
— | — | @@ -0,0 +1,3 @@ |
| 2 | +simple berkeleydb hack from memcached |
| 3 | +more information can be found at: |
| 4 | +http://meta.wikimedia.org/wiki/Tugela_Cache |
Property changes on: tags/tug_0_1/tugelacache/README |
___________________________________________________________________ |
Added: svn:keywords |
1 | 5 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 6 | + native |
Index: tags/tug_0_1/tugelacache/expire.c |
— | — | @@ -0,0 +1,106 @@ |
| 2 | +/* $Id$ |
| 3 | + |
| 4 | + Simple database cleanup script, gives out a feed of delete commands to standard output, |
| 5 | + suitable for redirection to tugela cache. |
| 6 | + |
| 7 | + TODO: Make it work with network. More run-time tuning params. |
| 8 | + |
| 9 | +*/ |
| 10 | +#include <sys/types.h> |
| 11 | +#include <sys/time.h> |
| 12 | +#include <sys/socket.h> |
| 13 | +#include <pwd.h> |
| 14 | +#include <fcntl.h> |
| 15 | +#include <stdlib.h> |
| 16 | +#include <stdio.h> |
| 17 | +#include <string.h> |
| 18 | +#include <unistd.h> |
| 19 | +#include <netinet/in.h> |
| 20 | +#include <netinet/tcp.h> |
| 21 | +#include <arpa/inet.h> |
| 22 | +#include <errno.h> |
| 23 | +#include <time.h> |
| 24 | +#include <event.h> |
| 25 | +#include <db.h> |
| 26 | +#include "dbcached.h" |
| 27 | + |
| 28 | +#define PARTSIZE 40; |
| 29 | + |
| 30 | +void usage(); |
| 31 | + |
| 32 | +int main(int argc, char **argv) |
| 33 | +{ |
| 34 | + DB *dbp; |
| 35 | + DBC *dbcp; |
| 36 | + DBT key, data; |
| 37 | + char *dbfile = DBFILE; |
| 38 | + char *prefix = NULL; |
| 39 | + size_t prefixlen = 0; |
| 40 | + int ret; |
| 41 | + item *it; |
| 42 | + time_t oldest = 0; |
| 43 | + time_t now; |
| 44 | + int c; |
| 45 | + |
| 46 | + now = time(NULL); |
| 47 | + while ((c = getopt(argc, argv, "f:ho:p:")) != -1) { |
| 48 | + switch (c) { |
| 49 | + case 'f': |
| 50 | + dbfile = optarg; |
| 51 | + break; |
| 52 | + case 'o': |
| 53 | + oldest = now - (86400 * atoi(optarg)); |
| 54 | + break; |
| 55 | + case 'p': |
| 56 | + prefix = optarg; |
| 57 | + prefixlen = strlen(prefix); |
| 58 | + break; |
| 59 | + default: |
| 60 | + usage(); |
| 61 | + exit(1); |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + db_create(&dbp, NULL, 0); |
| 66 | + if ((ret = dbp->open(dbp, NULL, dbfile, NULL, |
| 67 | + DB_BTREE, DB_RDONLY, 0644)) != 0) { |
| 68 | + dbp->err(dbp, ret, "%s", dbfile); |
| 69 | + exit(1); |
| 70 | + } |
| 71 | + |
| 72 | + if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) { |
| 73 | + dbp->err(dbp, ret, "DB->cursor"); |
| 74 | + exit(1); |
| 75 | + } |
| 76 | + |
| 77 | + memset(&key, 0, sizeof(key)); |
| 78 | + memset(&data, 0, sizeof(data)); |
| 79 | + data.flags = DB_DBT_PARTIAL; |
| 80 | + data.doff = 0; |
| 81 | + data.dlen = PARTSIZE; |
| 82 | + |
| 83 | + while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) { |
| 84 | + it = data.data; |
| 85 | + if (prefix |
| 86 | + && (key.size < prefixlen |
| 87 | + || strncmp(prefix, key.data, prefixlen))) |
| 88 | + continue; |
| 89 | + if ((it->exptime && it->exptime <= now) || it->time < oldest) |
| 90 | + printf("delete %.*s\n", |
| 91 | + (int) key.size, (char *) key.data, it->exptime); |
| 92 | + } |
| 93 | + if (ret != DB_NOTFOUND) { |
| 94 | + dbp->err(dbp, ret, "DBcursor->get"); |
| 95 | + exit(1); |
| 96 | + } |
| 97 | + exit(0); |
| 98 | +} |
| 99 | + |
| 100 | +void usage() |
| 101 | +{ |
| 102 | + printf("-f file database file\n"); |
| 103 | + printf("-p prefix key prefix\n"); |
| 104 | + printf |
| 105 | + ("-o days remove items older than specified (default: no limit)\n"); |
| 106 | + |
| 107 | +} |
Property changes on: tags/tug_0_1/tugelacache/expire.c |
___________________________________________________________________ |
Added: svn:keywords |
1 | 108 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 109 | + native |
Index: tags/tug_0_1/tugelacache/dbcached.c |
— | — | @@ -0,0 +1,1497 @@ |
| 2 | +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
| 3 | +/* |
| 4 | + * dbcached - object store daemon |
| 5 | + * |
| 6 | + * Copyright 2003 Danga Interactive, Inc. All rights reserved. |
| 7 | + * Copyright 2004 Domas Mituzas. All rights reserved. |
| 8 | + * |
| 9 | + * Use and distribution licensed under the BSD license. See |
| 10 | + * the LICENSE file for full text. |
| 11 | + * |
| 12 | + * Authors: |
| 13 | + * Anatoly Vorobey <mellon@pobox.com> |
| 14 | + * Brad Fitzpatrick <brad@danga.com> |
| 15 | + * Domas Mituzas <domas.mituzas@gmail.com> |
| 16 | + * |
| 17 | + * $Id$ |
| 18 | + */ |
| 19 | + |
| 20 | +#include "config.h" |
| 21 | +#include <sys/types.h> |
| 22 | +#include <sys/stat.h> |
| 23 | +#include <sys/time.h> |
| 24 | +#include <sys/socket.h> |
| 25 | +#include <sys/resource.h> |
| 26 | +/* some POSIX systems need the following definition |
| 27 | + * to get mlockall flags out of sys/mman.h. */ |
| 28 | +#ifndef _P1003_1B_VISIBLE |
| 29 | +#define _P1003_1B_VISIBLE |
| 30 | +#endif |
| 31 | +#include <pwd.h> |
| 32 | +#include <sys/mman.h> |
| 33 | +#include <fcntl.h> |
| 34 | +#include <stdlib.h> |
| 35 | +#include <stdio.h> |
| 36 | +#include <string.h> |
| 37 | +#include <unistd.h> |
| 38 | +#include <netinet/in.h> |
| 39 | +#include <netinet/tcp.h> |
| 40 | +#include <arpa/inet.h> |
| 41 | +#include <errno.h> |
| 42 | +#include <time.h> |
| 43 | +#include <event.h> |
| 44 | +#include <assert.h> |
| 45 | +#include <db.h> |
| 46 | +#include <signal.h> |
| 47 | + |
| 48 | + |
| 49 | +#ifdef HAVE_MALLOC_H |
| 50 | +#include <malloc.h> |
| 51 | +#endif |
| 52 | + |
| 53 | +#include "dbcached.h" |
| 54 | + |
| 55 | +struct stats stats; |
| 56 | +struct settings settings; |
| 57 | + |
| 58 | +DB *dbp; |
| 59 | +DBT dbkey, dbdata; |
| 60 | + |
| 61 | +time_t realtime(time_t exptime) |
| 62 | +{ |
| 63 | + time_t now; |
| 64 | + |
| 65 | + /* no. of seconds in 30 days - largest possible delta exptime */ |
| 66 | +#define REALTIME_MAXDELTA 60*60*24*30 |
| 67 | + |
| 68 | + if (exptime == 0) |
| 69 | + return 0; /* 0 means never expire */ |
| 70 | + |
| 71 | + if (exptime > REALTIME_MAXDELTA) |
| 72 | + return exptime; |
| 73 | + else { |
| 74 | + now = time(0); |
| 75 | + return exptime + now; |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +void stats_init(void) |
| 80 | +{ |
| 81 | + stats.curr_items = stats.total_items = stats.curr_conns = |
| 82 | + stats.total_conns = stats.conn_structs = 0; |
| 83 | + stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = |
| 84 | + 0; |
| 85 | + stats.curr_bytes = stats.bytes_read = stats.bytes_written = 0; |
| 86 | + stats.started = time(0); |
| 87 | +} |
| 88 | + |
| 89 | +void stats_reset(void) |
| 90 | +{ |
| 91 | + stats.total_items = stats.total_conns = 0; |
| 92 | + stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = |
| 93 | + 0; |
| 94 | + stats.bytes_read = stats.bytes_written = 0; |
| 95 | +} |
| 96 | + |
| 97 | +void settings_init(void) |
| 98 | +{ |
| 99 | + settings.port = 11211; |
| 100 | + settings.interface.s_addr = htonl(INADDR_ANY); |
| 101 | + settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */ |
| 102 | + settings.maxconns = 1024; /* to limit connections-related memory to about 5MB */ |
| 103 | + settings.synctimer = 66; |
| 104 | + settings.verbose = 0; |
| 105 | + settings.oldest_live = 0; |
| 106 | +} |
| 107 | + |
| 108 | +conn **freeconns; |
| 109 | +int freetotal; |
| 110 | +int freecurr; |
| 111 | + |
| 112 | +void set_cork(conn * c, int val) |
| 113 | +{ |
| 114 | + if (c->is_corked == val) |
| 115 | + return; |
| 116 | + c->is_corked = val; |
| 117 | +#ifdef TCP_NOPUSH |
| 118 | + setsockopt(c->sfd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); |
| 119 | +#endif |
| 120 | +} |
| 121 | + |
| 122 | +void conn_init(void) |
| 123 | +{ |
| 124 | + freetotal = 200; |
| 125 | + freecurr = 0; |
| 126 | + freeconns = (conn **) malloc(sizeof(conn *) * freetotal); |
| 127 | + return; |
| 128 | +} |
| 129 | + |
| 130 | +conn *conn_new(int sfd, int init_state, int event_flags) |
| 131 | +{ |
| 132 | + conn *c; |
| 133 | + |
| 134 | + /* do we have a free conn structure from a previous close? */ |
| 135 | + if (freecurr > 0) { |
| 136 | + c = freeconns[--freecurr]; |
| 137 | + } else { /* allocate a new one */ |
| 138 | + if (!(c = (conn *) malloc(sizeof(conn)))) { |
| 139 | + perror("malloc()"); |
| 140 | + return 0; |
| 141 | + } |
| 142 | + c->rbuf = c->wbuf = 0; |
| 143 | + c->ilist = 0; |
| 144 | + |
| 145 | + c->rbuf = (char *) malloc(DATA_BUFFER_SIZE); |
| 146 | + c->wbuf = (char *) malloc(DATA_BUFFER_SIZE); |
| 147 | + c->ilist = (item **) malloc(sizeof(item *) * 200); |
| 148 | + |
| 149 | + if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0) { |
| 150 | + if (c->rbuf != 0) |
| 151 | + free(c->rbuf); |
| 152 | + if (c->wbuf != 0) |
| 153 | + free(c->wbuf); |
| 154 | + if (c->ilist != 0) |
| 155 | + free(c->ilist); |
| 156 | + free(c); |
| 157 | + perror("malloc()"); |
| 158 | + return 0; |
| 159 | + } |
| 160 | + c->rsize = c->wsize = DATA_BUFFER_SIZE; |
| 161 | + c->isize = 200; |
| 162 | + stats.conn_structs++; |
| 163 | + } |
| 164 | + |
| 165 | + if (settings.verbose > 1) { |
| 166 | + if (init_state == conn_listening) |
| 167 | + fprintf(stderr, "<%d server listening\n", sfd); |
| 168 | + else |
| 169 | + fprintf(stderr, "<%d new client connection\n", sfd); |
| 170 | + } |
| 171 | + |
| 172 | + c->sfd = sfd; |
| 173 | + c->state = init_state; |
| 174 | + c->rlbytes = 0; |
| 175 | + c->rbytes = c->wbytes = 0; |
| 176 | + c->wcurr = c->wbuf; |
| 177 | + c->rcurr = c->rbuf; |
| 178 | + c->icurr = c->ilist; |
| 179 | + c->ileft = 0; |
| 180 | + c->iptr = c->ibuf; |
| 181 | + c->ibytes = 0; |
| 182 | + |
| 183 | + c->write_and_go = conn_read; |
| 184 | + c->write_and_free = 0; |
| 185 | + c->item = 0; |
| 186 | + |
| 187 | + c->is_corked = 0; |
| 188 | + |
| 189 | + event_set(&c->event, sfd, event_flags, event_handler, (void *) c); |
| 190 | + c->ev_flags = event_flags; |
| 191 | + |
| 192 | + if (event_add(&c->event, 0) == -1) { |
| 193 | + free(c); |
| 194 | + return 0; |
| 195 | + } |
| 196 | + |
| 197 | + stats.curr_conns++; |
| 198 | + stats.total_conns++; |
| 199 | + |
| 200 | + return c; |
| 201 | +} |
| 202 | + |
| 203 | +void conn_close(conn * c) |
| 204 | +{ |
| 205 | + /* delete the event, the socket and the conn */ |
| 206 | + event_del(&c->event); |
| 207 | + |
| 208 | + if (settings.verbose > 1) |
| 209 | + fprintf(stderr, "<%d connection closed.\n", c->sfd); |
| 210 | + |
| 211 | + close(c->sfd); |
| 212 | + |
| 213 | + if (c->item) { |
| 214 | + item_free(c->item); |
| 215 | + } |
| 216 | + |
| 217 | + if (c->ileft) { |
| 218 | + for (; c->ileft > 0; c->ileft--, c->icurr++) { |
| 219 | + item_remove(*(c->icurr)); |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + if (c->write_and_free) { |
| 224 | + free(c->write_and_free); |
| 225 | + } |
| 226 | + |
| 227 | + /* if we have enough space in the free connections array, put the structure there */ |
| 228 | + if (freecurr < freetotal) { |
| 229 | + freeconns[freecurr++] = c; |
| 230 | + } else { |
| 231 | + /* try to enlarge free connections array */ |
| 232 | + conn **new_freeconns = |
| 233 | + realloc(freeconns, sizeof(conn *) * freetotal * 2); |
| 234 | + if (new_freeconns) { |
| 235 | + freetotal *= 2; |
| 236 | + freeconns = new_freeconns; |
| 237 | + freeconns[freecurr++] = c; |
| 238 | + } else { |
| 239 | + free(c->rbuf); |
| 240 | + free(c->wbuf); |
| 241 | + free(c->ilist); |
| 242 | + free(c); |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + stats.curr_conns--; |
| 247 | + |
| 248 | + return; |
| 249 | +} |
| 250 | + |
| 251 | +void out_string(conn * c, char *str) |
| 252 | +{ |
| 253 | + int len; |
| 254 | + |
| 255 | + if (settings.verbose > 1) |
| 256 | + fprintf(stderr, ">%d %s\n", c->sfd, str); |
| 257 | + |
| 258 | + len = strlen(str); |
| 259 | + if (len + 2 > c->wsize) { |
| 260 | + /* ought to be always enough. just fail for simplicity */ |
| 261 | + str = "SERVER_ERROR output line too long"; |
| 262 | + len = strlen(str); |
| 263 | + } |
| 264 | + |
| 265 | + strcpy(c->wbuf, str); |
| 266 | + strcat(c->wbuf, "\r\n"); |
| 267 | + c->wbytes = len + 2; |
| 268 | + c->wcurr = c->wbuf; |
| 269 | + |
| 270 | + c->state = conn_write; |
| 271 | + c->write_and_go = conn_read; |
| 272 | + return; |
| 273 | +} |
| 274 | + |
| 275 | +/* |
| 276 | + * we get here after reading the value in set/add/replace commands. The command |
| 277 | + * has been stored in c->item_comm, and the item is ready in c->item. |
| 278 | + */ |
| 279 | + |
| 280 | +void complete_nread(conn * c) |
| 281 | +{ |
| 282 | + item *it = c->item, *testit = NULL; |
| 283 | + time_t now; |
| 284 | + int comm = c->item_comm; |
| 285 | + u_int32_t dbflags = 0; |
| 286 | + int ret; |
| 287 | + |
| 288 | + stats.set_cmds++; |
| 289 | + now = time(0); |
| 290 | + |
| 291 | + while (1) { |
| 292 | + if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) { |
| 293 | + out_string(c, "CLIENT_ERROR bad data chunk"); |
| 294 | + break; |
| 295 | + } |
| 296 | + |
| 297 | + cleanup_dbt(); |
| 298 | + if (comm == NREAD_ADD || comm == NREAD_REPLACE) { |
| 299 | + dbkey.data = ITEM_key(it); |
| 300 | + dbkey.size = strlen(ITEM_key(it)); |
| 301 | + dbkey.dlen = 40; |
| 302 | + if ((ret = dbp->get(dbp, NULL, &dbkey, &dbdata, 0)) == 0) { |
| 303 | + /* old data exists */ |
| 304 | + testit = dbdata.data; |
| 305 | + if (testit && testit->exptime && testit->exptime < now) { |
| 306 | + /* expired */ |
| 307 | + if (comm == NREAD_REPLACE) { |
| 308 | + /* remove on replace, return */ |
| 309 | + dbp->del(dbp, NULL, &dbkey, 0); |
| 310 | + out_string(c, "NOT STORED"); |
| 311 | + break; |
| 312 | + } |
| 313 | + } else if (comm == NREAD_ADD) { |
| 314 | + /* don't overwrite not expired data */ |
| 315 | + dbflags |= DB_NOOVERWRITE; |
| 316 | + } |
| 317 | + } else if (comm == NREAD_REPLACE) { |
| 318 | + out_string(c, "NOT STORED"); |
| 319 | + break; |
| 320 | + } |
| 321 | + } |
| 322 | + |
| 323 | + cleanup_dbt(); |
| 324 | + dbkey.data = ITEM_key(it); |
| 325 | + dbkey.size = strlen(ITEM_key(it)); |
| 326 | + dbdata.data = it; |
| 327 | + dbdata.size = ITEM_ntotal(it); |
| 328 | + if ((ret = dbp->put(dbp, NULL, &dbkey, &dbdata, dbflags)) == 0) { |
| 329 | + /* some future code? */ |
| 330 | + out_string(c, "STORED"); |
| 331 | + |
| 332 | + } else { |
| 333 | + out_string(c, "NOT STORED"); |
| 334 | + } |
| 335 | + free(c->item); |
| 336 | + c->item = 0; |
| 337 | + return; |
| 338 | + } |
| 339 | + |
| 340 | + item_free(it); |
| 341 | + c->item = 0; |
| 342 | + return; |
| 343 | +} |
| 344 | + |
| 345 | +void process_stat(conn * c, char *command) |
| 346 | +{ |
| 347 | + time_t now = time(0); |
| 348 | + |
| 349 | + if (strcmp(command, "stats") == 0) { |
| 350 | + char temp[1024]; |
| 351 | + pid_t pid = getpid(); |
| 352 | + char *pos = temp; |
| 353 | + struct rusage usage; |
| 354 | + |
| 355 | + getrusage(RUSAGE_SELF, &usage); |
| 356 | + |
| 357 | + pos += sprintf(pos, "STAT pid %u\r\n", pid); |
| 358 | + pos += sprintf(pos, "STAT uptime %lu\r\n", now - stats.started); |
| 359 | + pos += sprintf(pos, "STAT time %u\r\n", (unsigned int) now); |
| 360 | + pos += sprintf(pos, "STAT version " VERSION "\r\n"); |
| 361 | + pos += |
| 362 | + sprintf(pos, "STAT rusage_user %u:%u\r\n", |
| 363 | + (unsigned int) usage.ru_utime.tv_sec, |
| 364 | + (unsigned int) usage.ru_utime.tv_usec); |
| 365 | + pos += |
| 366 | + sprintf(pos, "STAT rusage_system %u:%u\r\n", |
| 367 | + (unsigned int) usage.ru_stime.tv_sec, |
| 368 | + (unsigned int) usage.ru_stime.tv_usec); |
| 369 | + pos += sprintf(pos, "STAT curr_items %u\r\n", stats.curr_items); |
| 370 | + pos += sprintf(pos, "STAT total_items %u\r\n", stats.total_items); |
| 371 | + pos += sprintf(pos, "STAT bytes %llu\r\n", stats.curr_bytes); |
| 372 | + pos += sprintf(pos, "STAT curr_connections %u\r\n", stats.curr_conns - 1); /* ignore listening conn */ |
| 373 | + pos += |
| 374 | + sprintf(pos, "STAT total_connections %u\r\n", |
| 375 | + stats.total_conns); |
| 376 | + pos += |
| 377 | + sprintf(pos, "STAT connection_structures %u\r\n", |
| 378 | + stats.conn_structs); |
| 379 | + pos += sprintf(pos, "STAT cmd_get %u\r\n", stats.get_cmds); |
| 380 | + pos += sprintf(pos, "STAT cmd_set %u\r\n", stats.set_cmds); |
| 381 | + pos += sprintf(pos, "STAT get_hits %u\r\n", stats.get_hits); |
| 382 | + pos += sprintf(pos, "STAT get_misses %u\r\n", stats.get_misses); |
| 383 | + pos += sprintf(pos, "STAT bytes_read %llu\r\n", stats.bytes_read); |
| 384 | + pos += |
| 385 | + sprintf(pos, "STAT bytes_written %llu\r\n", |
| 386 | + stats.bytes_written); |
| 387 | + pos += |
| 388 | + sprintf(pos, "STAT limit_maxbytes %u\r\n", settings.maxbytes); |
| 389 | + pos += sprintf(pos, "END"); |
| 390 | + out_string(c, temp); |
| 391 | + return; |
| 392 | + } |
| 393 | + |
| 394 | + if (strcmp(command, "stats reset") == 0) { |
| 395 | + stats_reset(); |
| 396 | + out_string(c, "RESET"); |
| 397 | + return; |
| 398 | + } |
| 399 | +#ifdef HAVE_MALLOC_H |
| 400 | +#ifdef HAVE_STRUCT_MALLINFO |
| 401 | + if (strcmp(command, "stats malloc") == 0) { |
| 402 | + char temp[512]; |
| 403 | + struct mallinfo info; |
| 404 | + char *pos = temp; |
| 405 | + |
| 406 | + info = mallinfo(); |
| 407 | + pos += sprintf(pos, "STAT arena_size %d\r\n", info.arena); |
| 408 | + pos += sprintf(pos, "STAT free_chunks %d\r\n", info.ordblks); |
| 409 | + pos += sprintf(pos, "STAT fastbin_blocks %d\r\n", info.smblks); |
| 410 | + pos += sprintf(pos, "STAT mmapped_regions %d\r\n", info.hblks); |
| 411 | + pos += sprintf(pos, "STAT mmapped_space %d\r\n", info.hblkhd); |
| 412 | + pos += sprintf(pos, "STAT max_total_alloc %d\r\n", info.usmblks); |
| 413 | + pos += sprintf(pos, "STAT fastbin_space %d\r\n", info.fsmblks); |
| 414 | + pos += sprintf(pos, "STAT total_alloc %d\r\n", info.uordblks); |
| 415 | + pos += sprintf(pos, "STAT total_free %d\r\n", info.fordblks); |
| 416 | + pos += |
| 417 | + sprintf(pos, "STAT releasable_space %d\r\nEND", info.keepcost); |
| 418 | + out_string(c, temp); |
| 419 | + return; |
| 420 | + } |
| 421 | +#endif /* HAVE_STRUCT_MALLINFO */ |
| 422 | +#endif /* HAVE_MALLOC_H */ |
| 423 | + |
| 424 | + if (strcmp(command, "stats maps") == 0) { |
| 425 | + char *wbuf; |
| 426 | + int wsize = 8192; /* should be enough */ |
| 427 | + int fd; |
| 428 | + int res; |
| 429 | + |
| 430 | + wbuf = (char *) malloc(wsize); |
| 431 | + if (wbuf == 0) { |
| 432 | + out_string(c, "SERVER_ERROR out of memory"); |
| 433 | + return; |
| 434 | + } |
| 435 | + |
| 436 | + fd = open("/proc/self/maps", O_RDONLY); |
| 437 | + if (fd == -1) { |
| 438 | + out_string(c, "SERVER_ERROR cannot open the maps file"); |
| 439 | + free(wbuf); |
| 440 | + return; |
| 441 | + } |
| 442 | + |
| 443 | + res = read(fd, wbuf, wsize - 6); /* 6 = END\r\n\0 */ |
| 444 | + if (res == wsize - 6) { |
| 445 | + out_string(c, "SERVER_ERROR buffer overflow"); |
| 446 | + free(wbuf); |
| 447 | + close(fd); |
| 448 | + return; |
| 449 | + } |
| 450 | + if (res == 0 || res == -1) { |
| 451 | + out_string(c, "SERVER_ERROR can't read the maps file"); |
| 452 | + free(wbuf); |
| 453 | + close(fd); |
| 454 | + return; |
| 455 | + } |
| 456 | + strcpy(wbuf + res, "END\r\n"); |
| 457 | + c->write_and_free = wbuf; |
| 458 | + c->wcurr = wbuf; |
| 459 | + c->wbytes = res + 6; |
| 460 | + c->state = conn_write; |
| 461 | + c->write_and_go = conn_read; |
| 462 | + close(fd); |
| 463 | + return; |
| 464 | + } |
| 465 | + |
| 466 | + if (strncmp(command, "stats cachedump", 15) == 0) { |
| 467 | + char *buf; |
| 468 | + unsigned int bytes, id, limit = 0; |
| 469 | + char *start = command + 15; |
| 470 | + if (sscanf(start, "%u %u\r\n", &id, &limit) < 1) { |
| 471 | + out_string(c, "CLIENT_ERROR bad command line"); |
| 472 | + return; |
| 473 | + } |
| 474 | + |
| 475 | + buf = item_cachedump(id, limit, &bytes); |
| 476 | + if (buf == 0) { |
| 477 | + out_string(c, "SERVER_ERROR out of memory"); |
| 478 | + return; |
| 479 | + } |
| 480 | + |
| 481 | + c->write_and_free = buf; |
| 482 | + c->wcurr = buf; |
| 483 | + c->wbytes = bytes; |
| 484 | + c->state = conn_write; |
| 485 | + c->write_and_go = conn_read; |
| 486 | + return; |
| 487 | + } |
| 488 | + |
| 489 | + if (strcmp(command, "stats sizes") == 0) { |
| 490 | + out_string(c, "SERVER_ERROR unimplemented"); |
| 491 | + } |
| 492 | + |
| 493 | + out_string(c, "ERROR"); |
| 494 | +} |
| 495 | + |
| 496 | +void process_command(conn * c, char *command) |
| 497 | +{ |
| 498 | + |
| 499 | + int comm = 0; |
| 500 | + int incr = 0; |
| 501 | + |
| 502 | + /* |
| 503 | + * for commands set/add/replace, we build an item and read the data |
| 504 | + * directly into it, then continue in nread_complete(). |
| 505 | + */ |
| 506 | + |
| 507 | + if (settings.verbose > 1) |
| 508 | + fprintf(stderr, "<%d %s\n", c->sfd, command); |
| 509 | + |
| 510 | + /* All incoming commands will require a response, so we cork at the beginning, |
| 511 | + and uncork at the very end (usually by means of out_string) */ |
| 512 | + set_cork(c, 1); |
| 513 | + |
| 514 | + if ((strncmp(command, "add ", 4) == 0 && (comm = NREAD_ADD)) || |
| 515 | + (strncmp(command, "set ", 4) == 0 && (comm = NREAD_SET)) || |
| 516 | + (strncmp(command, "replace ", 8) == 0 && (comm = NREAD_REPLACE))) { |
| 517 | + |
| 518 | + char key[251]; |
| 519 | + int flags; |
| 520 | + time_t expire; |
| 521 | + int len, res; |
| 522 | + item *it; |
| 523 | + |
| 524 | + res = |
| 525 | + sscanf(command, "%*s %250s %u %lu %d\n", key, &flags, &expire, |
| 526 | + &len); |
| 527 | + if (res != 4 || strlen(key) == 0) { |
| 528 | + out_string(c, "CLIENT_ERROR bad command line format"); |
| 529 | + return; |
| 530 | + } |
| 531 | + expire = realtime(expire); |
| 532 | + it = item_alloc(key, flags, expire, len + 2); |
| 533 | + if (it == 0) { |
| 534 | + out_string(c, "SERVER_ERROR out of memory"); |
| 535 | + /* swallow the data line */ |
| 536 | + c->write_and_go = conn_swallow; |
| 537 | + c->sbytes = len + 2; |
| 538 | + return; |
| 539 | + } |
| 540 | + |
| 541 | + c->item_comm = comm; |
| 542 | + c->item = it; |
| 543 | + c->rcurr = ITEM_data(it); |
| 544 | + c->rlbytes = it->nbytes; |
| 545 | + c->state = conn_nread; |
| 546 | + return; |
| 547 | + } |
| 548 | + |
| 549 | + if ((strncmp(command, "incr ", 5) == 0 && (incr = 1)) || |
| 550 | + (strncmp(command, "decr ", 5) == 0)) { |
| 551 | + char temp[32]; |
| 552 | + unsigned int value; |
| 553 | + item *it, *newit = NULL, *putit = NULL; |
| 554 | + unsigned int delta; |
| 555 | + char key[251]; |
| 556 | + int res, ret; |
| 557 | + char *ptr; |
| 558 | + |
| 559 | + res = sscanf(command, "%*s %250s %u\n", key, &delta); |
| 560 | + if (res != 2 || strlen(key) == 0) { |
| 561 | + out_string(c, "CLIENT_ERROR bad command line format"); |
| 562 | + return; |
| 563 | + } |
| 564 | + |
| 565 | + cleanup_dbt(); |
| 566 | + dbkey.data = key; |
| 567 | + dbkey.size = strlen(key); |
| 568 | + if ((ret = dbp->get(dbp, NULL, &dbkey, &dbdata, 0)) == 0) { |
| 569 | + it = dbdata.data; |
| 570 | + } else { |
| 571 | + it = 0; |
| 572 | + } |
| 573 | + |
| 574 | + if (!it) { |
| 575 | + out_string(c, "NOT_FOUND"); |
| 576 | + return; |
| 577 | + } |
| 578 | + |
| 579 | + ptr = ITEM_data(it); |
| 580 | + while (*ptr && (*ptr < '0' && *ptr > '9')) |
| 581 | + ptr++; |
| 582 | + |
| 583 | + value = atoi(ptr); |
| 584 | + |
| 585 | + if (incr) |
| 586 | + value += delta; |
| 587 | + else { |
| 588 | + if (delta >= value) |
| 589 | + value = 0; |
| 590 | + else |
| 591 | + value -= delta; |
| 592 | + } |
| 593 | + |
| 594 | + sprintf(temp, "%u", value); |
| 595 | + res = strlen(temp); |
| 596 | + it->time=time(0); |
| 597 | + if (res + 2 > it->nbytes) { |
| 598 | + newit = |
| 599 | + item_alloc(ITEM_key(it), it->flags, it->exptime, res + 2); |
| 600 | + memcpy(ITEM_data(newit), temp, res); |
| 601 | + memcpy(ITEM_data(newit) + res, "\r\n", 2); |
| 602 | + putit = newit; |
| 603 | + } else { |
| 604 | + memcpy(ITEM_data(it), temp, res); |
| 605 | + memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2); |
| 606 | + putit = it; |
| 607 | + } |
| 608 | + cleanup_dbt(); |
| 609 | + dbkey.data = key; |
| 610 | + dbkey.size = strlen(key); |
| 611 | + dbdata.data = putit; |
| 612 | + dbdata.size = ITEM_ntotal(putit); |
| 613 | + dbp->put(dbp, NULL, &dbkey, &dbdata, 0); |
| 614 | + if (newit) |
| 615 | + free(newit); |
| 616 | + out_string(c, temp); |
| 617 | + return; |
| 618 | + } |
| 619 | + |
| 620 | + if (strncmp(command, "get ", 4) == 0) { |
| 621 | + |
| 622 | + char *start = command + 4; |
| 623 | + char key[251]; |
| 624 | + int next; |
| 625 | + int i = 0; |
| 626 | + int ret; |
| 627 | + item *it; |
| 628 | + time_t now = time(0); |
| 629 | + |
| 630 | + while (sscanf(start, " %250s%n", key, &next) >= 1) { |
| 631 | + start += next; |
| 632 | + stats.get_cmds++; |
| 633 | + cleanup_dbt(); |
| 634 | + dbkey.data = key; |
| 635 | + dbkey.size = strlen(key); |
| 636 | + dbdata.flags |= DB_DBT_MALLOC; |
| 637 | + if ((ret = dbp->get(dbp, NULL, &dbkey, &dbdata, 0)) == 0) { |
| 638 | + it = dbdata.data; |
| 639 | + } else { |
| 640 | + it = 0; |
| 641 | + } |
| 642 | + if (settings.oldest_live && it && |
| 643 | + it->time <= settings.oldest_live) { |
| 644 | + dbp->del(dbp, NULL, &dbkey, 0); |
| 645 | + free(it); |
| 646 | + it = 0; |
| 647 | + } |
| 648 | + if (it && it->exptime && it->exptime < now) { |
| 649 | + dbp->del(dbp, NULL, &dbkey, 0); |
| 650 | + free(it); |
| 651 | + it = 0; |
| 652 | + } |
| 653 | + if (it) { |
| 654 | + stats.get_hits++; |
| 655 | + it->refcount++; |
| 656 | + *(c->ilist + i) = it; |
| 657 | + i++; |
| 658 | + if (i > c->isize) { |
| 659 | + c->isize *= 2; |
| 660 | + c->ilist = |
| 661 | + realloc(c->ilist, sizeof(item *) * c->isize); |
| 662 | + } |
| 663 | + } else |
| 664 | + stats.get_misses++; |
| 665 | + } |
| 666 | + c->icurr = c->ilist; |
| 667 | + c->ileft = i; |
| 668 | + if (c->ileft) { |
| 669 | + c->ipart = 0; |
| 670 | + c->state = conn_mwrite; |
| 671 | + c->ibytes = 0; |
| 672 | + return; |
| 673 | + } else { |
| 674 | + out_string(c, "END"); |
| 675 | + return; |
| 676 | + } |
| 677 | + } |
| 678 | + |
| 679 | + if (strncmp(command, "delete ", 7) == 0) { |
| 680 | + char key[251]; |
| 681 | + int res; |
| 682 | + int ret; |
| 683 | + time_t exptime = 0; |
| 684 | + |
| 685 | + res = sscanf(command, "%*s %250s %d", key, (int *) &exptime); |
| 686 | + cleanup_dbt(); |
| 687 | + dbkey.data = key; |
| 688 | + dbkey.size = strlen(key); |
| 689 | + if ((ret = dbp->del(dbp, NULL, &dbkey, 0)) == 0) { |
| 690 | + out_string(c, "DELETED"); |
| 691 | + } else { |
| 692 | + out_string(c, "NOT_FOUND"); |
| 693 | + } |
| 694 | + return; |
| 695 | + } |
| 696 | + |
| 697 | + if (strncmp(command, "stats", 5) == 0) { |
| 698 | + process_stat(c, command); |
| 699 | + return; |
| 700 | + } |
| 701 | + |
| 702 | + if (strcmp(command, "flush_all") == 0) { |
| 703 | + settings.oldest_live = time(0); |
| 704 | + out_string(c, "OK"); |
| 705 | + return; |
| 706 | + } |
| 707 | + |
| 708 | + if (strcmp(command, "version") == 0) { |
| 709 | + out_string(c, "VERSION " VERSION); |
| 710 | + return; |
| 711 | + } |
| 712 | + |
| 713 | + if (strcmp(command, "sync") == 0) { |
| 714 | + dbp->sync(dbp, 0); |
| 715 | + out_string(c, "OK"); |
| 716 | + return; |
| 717 | + } |
| 718 | + |
| 719 | + if (strcmp(command, "quit") == 0) { |
| 720 | + c->state = conn_closing; |
| 721 | + return; |
| 722 | + } |
| 723 | + |
| 724 | + out_string(c, "ERROR"); |
| 725 | + return; |
| 726 | +} |
| 727 | + |
| 728 | +/* |
| 729 | + * if we have a complete line in the buffer, process it and move whatever |
| 730 | + * remains in the buffer to its beginning. |
| 731 | + */ |
| 732 | +int try_read_command(conn * c) |
| 733 | +{ |
| 734 | + char *el, *cont; |
| 735 | + |
| 736 | + if (!c->rbytes) |
| 737 | + return 0; |
| 738 | + el = memchr(c->rbuf, '\n', c->rbytes); |
| 739 | + if (!el) |
| 740 | + return 0; |
| 741 | + cont = el + 1; |
| 742 | + if (el - c->rbuf > 1 && *(el - 1) == '\r') { |
| 743 | + el--; |
| 744 | + } |
| 745 | + *el = '\0'; |
| 746 | + |
| 747 | + process_command(c, c->rbuf); |
| 748 | + |
| 749 | + if (cont - c->rbuf < c->rbytes) { /* more stuff in the buffer */ |
| 750 | + memmove(c->rbuf, cont, c->rbytes - (cont - c->rbuf)); |
| 751 | + } |
| 752 | + c->rbytes -= (cont - c->rbuf); |
| 753 | + return 1; |
| 754 | +} |
| 755 | + |
| 756 | +/* |
| 757 | + * read from network as much as we can, handle buffer overflow and connection |
| 758 | + * close. |
| 759 | + * return 0 if there's nothing to read on the first read. |
| 760 | + */ |
| 761 | +int try_read_network(conn * c) |
| 762 | +{ |
| 763 | + int gotdata = 0; |
| 764 | + int res; |
| 765 | + while (1) { |
| 766 | + if (c->rbytes >= c->rsize) { |
| 767 | + char *new_rbuf = realloc(c->rbuf, c->rsize * 2); |
| 768 | + if (!new_rbuf) { |
| 769 | + if (settings.verbose > 0) |
| 770 | + fprintf(stderr, "Couldn't realloc input buffer\n"); |
| 771 | + c->rbytes = 0; /* ignore what we read */ |
| 772 | + out_string(c, "SERVER_ERROR out of memory"); |
| 773 | + c->write_and_go = conn_closing; |
| 774 | + return 1; |
| 775 | + } |
| 776 | + c->rbuf = new_rbuf; |
| 777 | + c->rsize *= 2; |
| 778 | + } |
| 779 | + res = read(c->sfd, c->rbuf + c->rbytes, c->rsize - c->rbytes); |
| 780 | + if (res > 0) { |
| 781 | + stats.bytes_read += res; |
| 782 | + gotdata = 1; |
| 783 | + c->rbytes += res; |
| 784 | + continue; |
| 785 | + } |
| 786 | + if (res == 0) { |
| 787 | + /* connection closed */ |
| 788 | + c->state = conn_closing; |
| 789 | + return 1; |
| 790 | + } |
| 791 | + if (res == -1) { |
| 792 | + if (errno == EAGAIN || errno == EWOULDBLOCK) |
| 793 | + break; |
| 794 | + else |
| 795 | + return 0; |
| 796 | + } |
| 797 | + } |
| 798 | + return gotdata; |
| 799 | +} |
| 800 | + |
| 801 | +int update_event(conn * c, int new_flags) |
| 802 | +{ |
| 803 | + if (c->ev_flags == new_flags) |
| 804 | + return 1; |
| 805 | + if (event_del(&c->event) == -1) |
| 806 | + return 0; |
| 807 | + event_set(&c->event, c->sfd, new_flags, event_handler, (void *) c); |
| 808 | + c->ev_flags = new_flags; |
| 809 | + if (event_add(&c->event, 0) == -1) |
| 810 | + return 0; |
| 811 | + return 1; |
| 812 | +} |
| 813 | + |
| 814 | +void drive_machine(conn * c) |
| 815 | +{ |
| 816 | + |
| 817 | + int exit = 0; |
| 818 | + int sfd, flags = 1; |
| 819 | + socklen_t addrlen; |
| 820 | + struct sockaddr addr; |
| 821 | + conn *newc; |
| 822 | + int res; |
| 823 | + |
| 824 | + while (!exit) { |
| 825 | + /* printf("state %d\n", c->state); */ |
| 826 | + switch (c->state) { |
| 827 | + case conn_listening: |
| 828 | + addrlen = sizeof(addr); |
| 829 | + if ((sfd = accept(c->sfd, &addr, &addrlen)) == -1) { |
| 830 | + if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| 831 | + exit = 1; |
| 832 | + break; |
| 833 | + } else { |
| 834 | + perror("accept()"); |
| 835 | + } |
| 836 | + break; |
| 837 | + } |
| 838 | + if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || |
| 839 | + fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) { |
| 840 | + perror("setting O_NONBLOCK"); |
| 841 | + close(sfd); |
| 842 | + break; |
| 843 | + } |
| 844 | + newc = conn_new(sfd, conn_read, EV_READ | EV_PERSIST); |
| 845 | + if (!newc) { |
| 846 | + if (settings.verbose > 0) |
| 847 | + fprintf(stderr, "couldn't create new connection\n"); |
| 848 | + close(sfd); |
| 849 | + break; |
| 850 | + } |
| 851 | + |
| 852 | + break; |
| 853 | + |
| 854 | + case conn_read: |
| 855 | + if (try_read_command(c)) { |
| 856 | + continue; |
| 857 | + } |
| 858 | + if (try_read_network(c)) { |
| 859 | + continue; |
| 860 | + } |
| 861 | + /* we have no command line and no data to read from network */ |
| 862 | + if (!update_event(c, EV_READ | EV_PERSIST)) { |
| 863 | + if (settings.verbose > 0) |
| 864 | + fprintf(stderr, "Couldn't update event\n"); |
| 865 | + c->state = conn_closing; |
| 866 | + break; |
| 867 | + } |
| 868 | + exit = 1; |
| 869 | + break; |
| 870 | + |
| 871 | + case conn_nread: |
| 872 | + /* we are reading rlbytes into rcurr; */ |
| 873 | + if (c->rlbytes == 0) { |
| 874 | + complete_nread(c); |
| 875 | + break; |
| 876 | + } |
| 877 | + /* first check if we have leftovers in the conn_read buffer */ |
| 878 | + if (c->rbytes > 0) { |
| 879 | + int tocopy = |
| 880 | + c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes; |
| 881 | + memcpy(c->rcurr, c->rbuf, tocopy); |
| 882 | + c->rcurr += tocopy; |
| 883 | + c->rlbytes -= tocopy; |
| 884 | + if (c->rbytes > tocopy) { |
| 885 | + memmove(c->rbuf, c->rbuf + tocopy, c->rbytes - tocopy); |
| 886 | + } |
| 887 | + c->rbytes -= tocopy; |
| 888 | + break; |
| 889 | + } |
| 890 | + |
| 891 | + /* now try reading from the socket */ |
| 892 | + res = read(c->sfd, c->rcurr, c->rlbytes); |
| 893 | + if (res > 0) { |
| 894 | + stats.bytes_read += res; |
| 895 | + c->rcurr += res; |
| 896 | + c->rlbytes -= res; |
| 897 | + break; |
| 898 | + } |
| 899 | + if (res == 0) { /* end of stream */ |
| 900 | + c->state = conn_closing; |
| 901 | + break; |
| 902 | + } |
| 903 | + if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { |
| 904 | + if (!update_event(c, EV_READ | EV_PERSIST)) { |
| 905 | + if (settings.verbose > 0) |
| 906 | + fprintf(stderr, "Couldn't update event\n"); |
| 907 | + c->state = conn_closing; |
| 908 | + break; |
| 909 | + } |
| 910 | + exit = 1; |
| 911 | + break; |
| 912 | + } |
| 913 | + /* otherwise we have a real error, on which we close the connection */ |
| 914 | + if (settings.verbose > 0) |
| 915 | + fprintf(stderr, |
| 916 | + "Failed to read, and not due to blocking\n"); |
| 917 | + c->state = conn_closing; |
| 918 | + break; |
| 919 | + |
| 920 | + case conn_swallow: |
| 921 | + /* we are reading sbytes and throwing them away */ |
| 922 | + if (c->sbytes == 0) { |
| 923 | + c->state = conn_read; |
| 924 | + break; |
| 925 | + } |
| 926 | + |
| 927 | + /* first check if we have leftovers in the conn_read buffer */ |
| 928 | + if (c->rbytes > 0) { |
| 929 | + int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes; |
| 930 | + c->sbytes -= tocopy; |
| 931 | + if (c->rbytes > tocopy) { |
| 932 | + memmove(c->rbuf, c->rbuf + tocopy, c->rbytes - tocopy); |
| 933 | + } |
| 934 | + c->rbytes -= tocopy; |
| 935 | + break; |
| 936 | + } |
| 937 | + |
| 938 | + /* now try reading from the socket */ |
| 939 | + res = |
| 940 | + read(c->sfd, c->rbuf, |
| 941 | + c->rsize > c->sbytes ? c->sbytes : c->rsize); |
| 942 | + if (res > 0) { |
| 943 | + stats.bytes_read += res; |
| 944 | + c->sbytes -= res; |
| 945 | + break; |
| 946 | + } |
| 947 | + if (res == 0) { /* end of stream */ |
| 948 | + c->state = conn_closing; |
| 949 | + break; |
| 950 | + } |
| 951 | + if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { |
| 952 | + if (!update_event(c, EV_READ | EV_PERSIST)) { |
| 953 | + if (settings.verbose > 0) |
| 954 | + fprintf(stderr, "Couldn't update event\n"); |
| 955 | + c->state = conn_closing; |
| 956 | + break; |
| 957 | + } |
| 958 | + exit = 1; |
| 959 | + break; |
| 960 | + } |
| 961 | + /* otherwise we have a real error, on which we close the connection */ |
| 962 | + if (settings.verbose > 0) |
| 963 | + fprintf(stderr, |
| 964 | + "Failed to read, and not due to blocking\n"); |
| 965 | + c->state = conn_closing; |
| 966 | + break; |
| 967 | + |
| 968 | + case conn_write: |
| 969 | + /* we are writing wbytes bytes starting from wcurr */ |
| 970 | + if (c->wbytes == 0) { |
| 971 | + if (c->write_and_free) { |
| 972 | + free(c->write_and_free); |
| 973 | + c->write_and_free = 0; |
| 974 | + } |
| 975 | + c->state = c->write_and_go; |
| 976 | + if (c->state == conn_read) |
| 977 | + set_cork(c, 0); |
| 978 | + break; |
| 979 | + } |
| 980 | + res = write(c->sfd, c->wcurr, c->wbytes); |
| 981 | + if (res > 0) { |
| 982 | + stats.bytes_written += res; |
| 983 | + c->wcurr += res; |
| 984 | + c->wbytes -= res; |
| 985 | + break; |
| 986 | + } |
| 987 | + if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { |
| 988 | + if (!update_event(c, EV_WRITE | EV_PERSIST)) { |
| 989 | + if (settings.verbose > 0) |
| 990 | + fprintf(stderr, "Couldn't update event\n"); |
| 991 | + c->state = conn_closing; |
| 992 | + break; |
| 993 | + } |
| 994 | + exit = 1; |
| 995 | + break; |
| 996 | + } |
| 997 | + /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK, |
| 998 | + we have a real error, on which we close the connection */ |
| 999 | + if (settings.verbose > 0) |
| 1000 | + fprintf(stderr, |
| 1001 | + "Failed to write, and not due to blocking\n"); |
| 1002 | + c->state = conn_closing; |
| 1003 | + break; |
| 1004 | + case conn_mwrite: |
| 1005 | + /* |
| 1006 | + * we're writing ibytes bytes from iptr. iptr alternates between |
| 1007 | + * ibuf, where we build a string "VALUE...", and ITEM_data(it) for the |
| 1008 | + * current item. When we finish a chunk, we choose the next one using |
| 1009 | + * ipart, which has the following semantics: 0 - start the loop, 1 - |
| 1010 | + * we finished ibuf, go to current ITEM_data(it); 2 - we finished ITEM_data(it), |
| 1011 | + * move to the next item and build its ibuf; 3 - we finished all items, |
| 1012 | + * write "END". |
| 1013 | + */ |
| 1014 | + if (c->ibytes > 0) { |
| 1015 | + res = write(c->sfd, c->iptr, c->ibytes); |
| 1016 | + if (res > 0) { |
| 1017 | + stats.bytes_written += res; |
| 1018 | + c->iptr += res; |
| 1019 | + c->ibytes -= res; |
| 1020 | + break; |
| 1021 | + } |
| 1022 | + if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { |
| 1023 | + if (!update_event(c, EV_WRITE | EV_PERSIST)) { |
| 1024 | + if (settings.verbose > 0) |
| 1025 | + fprintf(stderr, "Couldn't update event\n"); |
| 1026 | + c->state = conn_closing; |
| 1027 | + break; |
| 1028 | + } |
| 1029 | + exit = 1; |
| 1030 | + break; |
| 1031 | + } |
| 1032 | + /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK, |
| 1033 | + we have a real error, on which we close the connection */ |
| 1034 | + if (settings.verbose > 0) |
| 1035 | + fprintf(stderr, |
| 1036 | + "Failed to write, and not due to blocking\n"); |
| 1037 | + c->state = conn_closing; |
| 1038 | + break; |
| 1039 | + } else { |
| 1040 | + item *it; |
| 1041 | + /* we finished a chunk, decide what to do next */ |
| 1042 | + switch (c->ipart) { |
| 1043 | + case 1: |
| 1044 | + it = *(c->icurr); |
| 1045 | + assert((it->it_flags & ITEM_SLABBED) == 0); |
| 1046 | + c->iptr = ITEM_data(it); |
| 1047 | + c->ibytes = it->nbytes; |
| 1048 | + c->ipart = 2; |
| 1049 | + break; |
| 1050 | + case 2: |
| 1051 | + it = *(c->icurr); |
| 1052 | + free(it); |
| 1053 | + c->ileft--; |
| 1054 | + if (c->ileft <= 0) { |
| 1055 | + c->ipart = 3; |
| 1056 | + break; |
| 1057 | + } else { |
| 1058 | + c->icurr++; |
| 1059 | + } |
| 1060 | + /* FALL THROUGH */ |
| 1061 | + case 0: |
| 1062 | + it = *(c->icurr); |
| 1063 | + sprintf(c->ibuf, "VALUE %s %u %u\r\n", ITEM_key(it), |
| 1064 | + it->flags, it->nbytes - 2); |
| 1065 | + if (settings.verbose > 1) |
| 1066 | + fprintf(stderr, ">%d sending key %s\n", c->sfd, |
| 1067 | + ITEM_key(it)); |
| 1068 | + c->iptr = c->ibuf; |
| 1069 | + c->ibytes = strlen(c->iptr); |
| 1070 | + c->ipart = 1; |
| 1071 | + break; |
| 1072 | + case 3: |
| 1073 | + out_string(c, "END"); |
| 1074 | + break; |
| 1075 | + } |
| 1076 | + } |
| 1077 | + break; |
| 1078 | + |
| 1079 | + case conn_closing: |
| 1080 | + conn_close(c); |
| 1081 | + exit = 1; |
| 1082 | + break; |
| 1083 | + } |
| 1084 | + |
| 1085 | + } |
| 1086 | + |
| 1087 | + return; |
| 1088 | +} |
| 1089 | + |
| 1090 | + |
| 1091 | +void event_handler(int fd, short which, void *arg) |
| 1092 | +{ |
| 1093 | + conn *c; |
| 1094 | + |
| 1095 | + c = (conn *) arg; |
| 1096 | + c->which = which; |
| 1097 | + |
| 1098 | + /* sanity */ |
| 1099 | + if (fd != c->sfd) { |
| 1100 | + if (settings.verbose > 0) |
| 1101 | + fprintf(stderr, |
| 1102 | + "Catastrophic: event fd doesn't match conn fd!\n"); |
| 1103 | + conn_close(c); |
| 1104 | + return; |
| 1105 | + } |
| 1106 | + |
| 1107 | + /* do as much I/O as possible until we block */ |
| 1108 | + drive_machine(c); |
| 1109 | + |
| 1110 | + /* wait for next event */ |
| 1111 | + return; |
| 1112 | +} |
| 1113 | + |
| 1114 | +int new_socket(void) |
| 1115 | +{ |
| 1116 | + int sfd; |
| 1117 | + int flags; |
| 1118 | + |
| 1119 | + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { |
| 1120 | + perror("socket()"); |
| 1121 | + return -1; |
| 1122 | + } |
| 1123 | + |
| 1124 | + if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || |
| 1125 | + fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) { |
| 1126 | + perror("setting O_NONBLOCK"); |
| 1127 | + close(sfd); |
| 1128 | + return -1; |
| 1129 | + } |
| 1130 | + return sfd; |
| 1131 | +} |
| 1132 | + |
| 1133 | +int server_socket(int port) |
| 1134 | +{ |
| 1135 | + int sfd; |
| 1136 | + struct linger ling = { 0, 0 }; |
| 1137 | + struct sockaddr_in addr; |
| 1138 | + int flags = 1; |
| 1139 | + |
| 1140 | + if ((sfd = new_socket()) == -1) { |
| 1141 | + return -1; |
| 1142 | + } |
| 1143 | + |
| 1144 | + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags)); |
| 1145 | + setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags)); |
| 1146 | + setsockopt(sfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); |
| 1147 | +#if !defined(TCP_NOPUSH) |
| 1148 | + setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags)); |
| 1149 | +#endif |
| 1150 | + |
| 1151 | + addr.sin_family = AF_INET; |
| 1152 | + addr.sin_port = htons(port); |
| 1153 | + addr.sin_addr = settings.interface; |
| 1154 | + if (bind(sfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { |
| 1155 | + perror("bind()"); |
| 1156 | + close(sfd); |
| 1157 | + return -1; |
| 1158 | + } |
| 1159 | + if (listen(sfd, 1024) == -1) { |
| 1160 | + perror("listen()"); |
| 1161 | + close(sfd); |
| 1162 | + return -1; |
| 1163 | + } |
| 1164 | + return sfd; |
| 1165 | +} |
| 1166 | + |
| 1167 | +/* invoke right before gdb is called, on assert */ |
| 1168 | +void pre_gdb() |
| 1169 | +{ |
| 1170 | + int i = 0; |
| 1171 | + if (l_socket) |
| 1172 | + close(l_socket); |
| 1173 | + for (i = 3; i <= 500; i++) |
| 1174 | + close(i); /* so lame */ |
| 1175 | + kill(getpid(), SIGABRT); |
| 1176 | +} |
| 1177 | + |
| 1178 | +struct event syncevent; |
| 1179 | + |
| 1180 | +void sync_handler(int fd, short which, void *arg) |
| 1181 | +{ |
| 1182 | + struct timeval t; |
| 1183 | + |
| 1184 | + evtimer_del(&syncevent); |
| 1185 | + evtimer_set(&syncevent, sync_handler, 0); |
| 1186 | + t.tv_sec = settings.synctimer; |
| 1187 | + t.tv_usec = 0; |
| 1188 | + evtimer_add(&syncevent, &t); |
| 1189 | + dbp->sync(dbp, 0); |
| 1190 | + return; |
| 1191 | +} |
| 1192 | + |
| 1193 | +void usage(void) |
| 1194 | +{ |
| 1195 | + printf(PACKAGE " " VERSION "\n"); |
| 1196 | + printf("-p <num> port number to listen on\n"); |
| 1197 | + printf("-l <ip_addr> interface to listen on, default is INDRR_ANY\n"); |
| 1198 | + printf("-d run as a daemon\n"); |
| 1199 | + printf("-r maximize core file limit\n"); |
| 1200 | + printf |
| 1201 | + ("-u <username> assume identity of <username> (only when run as root)\n"); |
| 1202 | + printf |
| 1203 | + ("-m <num> cache memory to use for items in megabytes, default is 64 MB\n"); |
| 1204 | + printf |
| 1205 | + ("-c <num> max simultaneous connections, default is 1024\n"); |
| 1206 | + printf("-f <file filename of database\n"); |
| 1207 | + printf("-s <num> sync this often seconds\n"); |
| 1208 | + printf |
| 1209 | + ("-v verbose (print errors/warnings while in event loop)\n"); |
| 1210 | + printf |
| 1211 | + ("-vv very verbose (also print client commands/reponses)\n"); |
| 1212 | + printf("-h print this help and exit\n"); |
| 1213 | + printf("-i print dbcached and libevent license\n"); |
| 1214 | + return; |
| 1215 | +} |
| 1216 | + |
| 1217 | +void usage_license(void) |
| 1218 | +{ |
| 1219 | + printf(PACKAGE " " VERSION "\n\n"); |
| 1220 | + printf |
| 1221 | + ("Copyright (c) 2003, Danga Interactive, Inc. <http://www.danga.com/>\n" |
| 1222 | + "All rights reserved.\n" "\n" |
| 1223 | + "Redistribution and use in source and binary forms, with or without\n" |
| 1224 | + "modification, are permitted provided that the following conditions are\n" |
| 1225 | + "met:\n" "\n" |
| 1226 | + " * Redistributions of source code must retain the above copyright\n" |
| 1227 | + "notice, this list of conditions and the following disclaimer.\n" |
| 1228 | + "\n" |
| 1229 | + " * Redistributions in binary form must reproduce the above\n" |
| 1230 | + "copyright notice, this list of conditions and the following disclaimer\n" |
| 1231 | + "in the documentation and/or other materials provided with the\n" |
| 1232 | + "distribution.\n" "\n" |
| 1233 | + " * Neither the name of the Danga Interactive nor the names of its\n" |
| 1234 | + "contributors may be used to endorse or promote products derived from\n" |
| 1235 | + "this software without specific prior written permission.\n" "\n" |
| 1236 | + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" |
| 1237 | + "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" |
| 1238 | + "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" |
| 1239 | + "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" |
| 1240 | + "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" |
| 1241 | + "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" |
| 1242 | + "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" |
| 1243 | + "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" |
| 1244 | + "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" |
| 1245 | + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" |
| 1246 | + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" |
| 1247 | + "\n" "\n" |
| 1248 | + "This product includes software developed by Niels Provos.\n" "\n" |
| 1249 | + "[ libevent ]\n" "\n" |
| 1250 | + "Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>\n" |
| 1251 | + "All rights reserved.\n" "\n" |
| 1252 | + "Redistribution and use in source and binary forms, with or without\n" |
| 1253 | + "modification, are permitted provided that the following conditions\n" |
| 1254 | + "are met:\n" |
| 1255 | + "1. Redistributions of source code must retain the above copyright\n" |
| 1256 | + " notice, this list of conditions and the following disclaimer.\n" |
| 1257 | + "2. Redistributions in binary form must reproduce the above copyright\n" |
| 1258 | + " notice, this list of conditions and the following disclaimer in the\n" |
| 1259 | + " documentation and/or other materials provided with the distribution.\n" |
| 1260 | + "3. All advertising materials mentioning features or use of this software\n" |
| 1261 | + " must display the following acknowledgement:\n" |
| 1262 | + " This product includes software developed by Niels Provos.\n" |
| 1263 | + "4. The name of the author may not be used to endorse or promote products\n" |
| 1264 | + " derived from this software without specific prior written permission.\n" |
| 1265 | + "\n" |
| 1266 | + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" |
| 1267 | + "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" |
| 1268 | + "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" |
| 1269 | + "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" |
| 1270 | + "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n" |
| 1271 | + "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" |
| 1272 | + "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" |
| 1273 | + "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" |
| 1274 | + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n" |
| 1275 | + "THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"); |
| 1276 | + |
| 1277 | + return; |
| 1278 | +} |
| 1279 | + |
| 1280 | +int l_socket = 0; |
| 1281 | + |
| 1282 | +int main(int argc, char **argv) |
| 1283 | +{ |
| 1284 | + int c; |
| 1285 | + conn *l_conn; |
| 1286 | + struct in_addr addr; |
| 1287 | + char *dbfile = DBFILE; |
| 1288 | + int daemonize = 0; |
| 1289 | + int maxcore = 0; |
| 1290 | + char *username = 0; |
| 1291 | + struct passwd *pw; |
| 1292 | + struct sigaction sa; |
| 1293 | + struct rlimit rlim; |
| 1294 | + int ret; |
| 1295 | + |
| 1296 | + /* init settings */ |
| 1297 | + settings_init(); |
| 1298 | + |
| 1299 | + /* process arguments */ |
| 1300 | + while ((c = getopt(argc, argv, "p:m:Mc:khirvdl:u:f:s:")) != -1) { |
| 1301 | + switch (c) { |
| 1302 | + case 'p': |
| 1303 | + settings.port = atoi(optarg); |
| 1304 | + break; |
| 1305 | + case 'm': |
| 1306 | + settings.maxbytes = atoi(optarg) * 1024 * 1024; |
| 1307 | + break; |
| 1308 | + case 'c': |
| 1309 | + settings.maxconns = atoi(optarg); |
| 1310 | + break; |
| 1311 | + case 'h': |
| 1312 | + usage(); |
| 1313 | + exit(0); |
| 1314 | + case 's': |
| 1315 | + settings.synctimer = atoi(optarg); |
| 1316 | + break; |
| 1317 | + case 'i': |
| 1318 | + usage_license(); |
| 1319 | + exit(0); |
| 1320 | + case 'f': |
| 1321 | + dbfile = optarg; |
| 1322 | + case 'v': |
| 1323 | + settings.verbose++; |
| 1324 | + break; |
| 1325 | + case 'l': |
| 1326 | + if (!inet_aton(optarg, &addr)) { |
| 1327 | + fprintf(stderr, "Illegal address: %s\n", optarg); |
| 1328 | + return 1; |
| 1329 | + } else { |
| 1330 | + settings.interface = addr; |
| 1331 | + } |
| 1332 | + break; |
| 1333 | + case 'd': |
| 1334 | + daemonize = 1; |
| 1335 | + break; |
| 1336 | + case 'r': |
| 1337 | + maxcore = 1; |
| 1338 | + break; |
| 1339 | + case 'u': |
| 1340 | + username = optarg; |
| 1341 | + break; |
| 1342 | + default: |
| 1343 | + fprintf(stderr, "Illegal argument \"%c\"\n", c); |
| 1344 | + return 1; |
| 1345 | + } |
| 1346 | + } |
| 1347 | + |
| 1348 | + if (maxcore) { |
| 1349 | + struct rlimit rlim_new; |
| 1350 | + /* |
| 1351 | + * First try raising to infinity; if that fails, try bringing |
| 1352 | + * the soft limit to the hard. |
| 1353 | + */ |
| 1354 | + if (getrlimit(RLIMIT_CORE, &rlim) == 0) { |
| 1355 | + rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY; |
| 1356 | + if (setrlimit(RLIMIT_CORE, &rlim_new) != 0) { |
| 1357 | + /* failed. try raising just to the old max */ |
| 1358 | + rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max; |
| 1359 | + (void) setrlimit(RLIMIT_CORE, &rlim_new); |
| 1360 | + } |
| 1361 | + } |
| 1362 | + /* |
| 1363 | + * getrlimit again to see what we ended up with. Only fail if |
| 1364 | + * the soft limit ends up 0, because then no core files will be |
| 1365 | + * created at all. |
| 1366 | + */ |
| 1367 | + |
| 1368 | + if ((getrlimit(RLIMIT_CORE, &rlim) != 0) || rlim.rlim_cur == 0) { |
| 1369 | + fprintf(stderr, "failed to ensure corefile creation\n"); |
| 1370 | + exit(1); |
| 1371 | + } |
| 1372 | + } |
| 1373 | + |
| 1374 | + /* |
| 1375 | + * If needed, increase rlimits to allow as many connections |
| 1376 | + * as needed. |
| 1377 | + */ |
| 1378 | + |
| 1379 | + if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { |
| 1380 | + fprintf(stderr, "failed to getrlimit number of files\n"); |
| 1381 | + exit(1); |
| 1382 | + } else { |
| 1383 | + int maxfiles = settings.maxconns; |
| 1384 | + if (rlim.rlim_cur < maxfiles) |
| 1385 | + rlim.rlim_cur = maxfiles + 3; |
| 1386 | + if (rlim.rlim_max < rlim.rlim_cur) |
| 1387 | + rlim.rlim_max = rlim.rlim_cur; |
| 1388 | + if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { |
| 1389 | + fprintf(stderr, |
| 1390 | + "failed to set rlimit for open files. Try running as root or requesting smaller maxconns value.\n"); |
| 1391 | + exit(1); |
| 1392 | + } |
| 1393 | + } |
| 1394 | + |
| 1395 | + /* |
| 1396 | + * initialization order: first create the listening socket |
| 1397 | + * (may need root on low ports), then drop root if needed, |
| 1398 | + * then daemonise if needed, then init libevent (in some cases |
| 1399 | + * descriptors created by libevent wouldn't survive forking). |
| 1400 | + */ |
| 1401 | + |
| 1402 | + if ((ret = db_create(&dbp, NULL, 0)) != 0) { |
| 1403 | + fprintf(stderr, "db_create: %s\n", db_strerror(ret)); |
| 1404 | + exit(1); |
| 1405 | + } |
| 1406 | + dbp->set_cachesize(dbp, 0, settings.maxbytes, 0); |
| 1407 | + |
| 1408 | + if ((ret = dbp->open(dbp, |
| 1409 | + NULL, dbfile, NULL, DB_BTREE, DB_CREATE, |
| 1410 | + 0664)) != 0) { |
| 1411 | + dbp->err(dbp, ret, "%s", dbfile); |
| 1412 | + exit(1); |
| 1413 | + } |
| 1414 | + |
| 1415 | + atexit(syncdb); |
| 1416 | + |
| 1417 | + /* create the listening socket and bind it */ |
| 1418 | + l_socket = server_socket(settings.port); |
| 1419 | + if (l_socket == -1) { |
| 1420 | + fprintf(stderr, "failed to listen\n"); |
| 1421 | + exit(1); |
| 1422 | + } |
| 1423 | + |
| 1424 | + /* lose root privileges if we have them */ |
| 1425 | + if (getuid() == 0 || geteuid() == 0) { |
| 1426 | + if (username == 0 || *username == '\0') { |
| 1427 | + fprintf(stderr, "can't run as root without the -u switch\n"); |
| 1428 | + return 1; |
| 1429 | + } |
| 1430 | + if ((pw = getpwnam(username)) == 0) { |
| 1431 | + fprintf(stderr, "can't find the user %s to switch to\n", |
| 1432 | + username); |
| 1433 | + return 1; |
| 1434 | + } |
| 1435 | + if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { |
| 1436 | + fprintf(stderr, "failed to assume identity of user %s\n", |
| 1437 | + username); |
| 1438 | + return 1; |
| 1439 | + } |
| 1440 | + } |
| 1441 | + |
| 1442 | + /* daemonize if requested */ |
| 1443 | + /* if we want to ensure our ability to dump core, don't chdir to / */ |
| 1444 | + if (daemonize) { |
| 1445 | + int res; |
| 1446 | + res = daemon(maxcore, settings.verbose); |
| 1447 | + if (res == -1) { |
| 1448 | + fprintf(stderr, "failed to daemon() in order to daemonize\n"); |
| 1449 | + return 1; |
| 1450 | + } |
| 1451 | + } |
| 1452 | + |
| 1453 | + |
| 1454 | + /* initialize other stuff */ |
| 1455 | + item_init(); |
| 1456 | + event_init(); |
| 1457 | + stats_init(); |
| 1458 | + assoc_init(); |
| 1459 | + conn_init(); |
| 1460 | + |
| 1461 | + /* |
| 1462 | + * ignore SIGPIPE signals; we can use errno==EPIPE if we |
| 1463 | + * need that information |
| 1464 | + */ |
| 1465 | + sa.sa_handler = SIG_IGN; |
| 1466 | + sa.sa_flags = 0; |
| 1467 | + if (sigemptyset(&sa.sa_mask) == -1 || sigaction(SIGPIPE, &sa, 0) == -1) { |
| 1468 | + perror("failed to ignore SIGPIPE; sigaction"); |
| 1469 | + exit(1); |
| 1470 | + } |
| 1471 | + |
| 1472 | + /* create the initial listening connection */ |
| 1473 | + if (! |
| 1474 | + (l_conn = |
| 1475 | + conn_new(l_socket, conn_listening, EV_READ | EV_PERSIST))) { |
| 1476 | + fprintf(stderr, "failed to create listening connection"); |
| 1477 | + exit(1); |
| 1478 | + } |
| 1479 | + |
| 1480 | + /* initialise deletion array and timer event */ |
| 1481 | + sync_handler(0, 0, 0); /* sets up the event */ |
| 1482 | + |
| 1483 | + /* enter the loop */ |
| 1484 | + event_loop(0); |
| 1485 | + |
| 1486 | + return 0; |
| 1487 | +} |
| 1488 | + |
| 1489 | +void cleanup_dbt() |
| 1490 | +{ |
| 1491 | + memset(&dbkey, 0, sizeof(dbkey)); |
| 1492 | + memset(&dbdata, 0, sizeof(dbdata)); |
| 1493 | +} |
| 1494 | + |
| 1495 | +void syncdb() |
| 1496 | +{ |
| 1497 | + dbp->sync(dbp, 0); |
| 1498 | +} |
Property changes on: tags/tug_0_1/tugelacache/dbcached.c |
___________________________________________________________________ |
Added: svn:keywords |
1 | 1499 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 1500 | + native |
Property changes on: tags/tug_0_1/tugelacache |
___________________________________________________________________ |
Added: svn:ignore |
3 | 1501 | + *~ |
*.o |
tugela |
dbcache |
tugela-expire |