├── .gitignore ├── ChangeLog ├── LICENSE ├── Makefile ├── README.md ├── TODO └── src ├── Makefile ├── ae.c ├── ae.h ├── ae_epoll.c ├── ae_kqueue.c ├── ae_select.c ├── anet.c ├── anet.h ├── config.h ├── fmacros.h ├── policy.c ├── policy.h ├── policy.rl ├── tcproxy.c ├── util.c ├── util.h ├── zmalloc.c └── zmalloc.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.DS_Store 3 | *.log 4 | cachegrind.out.* 5 | callgrind.out.* 6 | massif.out.* 7 | memcheck.out 8 | tcproxy 9 | core 10 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2011-06-2 Release 0.2.2 2 | * fix a bug that causes infinit loop when process write 3 | 4 | 2011-05-21 Release 0.2.1 5 | * fix ip address parse bug 6 | 7 | 2011-05-20 Release 0.2 8 | * nonblock connect implemented 9 | * semicolon bind to address not port now 10 | * some performance optimization 11 | * some bugfix 12 | 13 | 2011-05-18 Release 0.1.1 14 | * -l option added to write log file 15 | * minor code refinement 16 | 17 | 2011-05-16 Release 0.1 18 | * first public beta release 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright dccmx. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # tcproxy - Makefile 3 | # 4 | # Author: dccmx 5 | # 6 | 7 | default: all 8 | 9 | .DEFAULT: 10 | cd src && $(MAKE) $@ 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tcproxy 2 | ======= 3 | tcproxy is a small efficient tcp proxy that can be used for port forwarding or load balancing. 4 | 5 | sample usage 6 | ------------ 7 | tcproxy "11212 -> 11211" 8 | tcproxy "192.168.0.1:11212 -> 192.168.0.2:11211" 9 | 10 | not implemented yet 11 | --------------- 12 | tcproxy "any:11212 -> rr{192.168.0.100:11211 192.168.0.101:11211 192.168.0.102:11211}" 13 | tcproxy "any:11212 -> hash{192.168.0.100:11211 192.168.0.101:11211 192.168.0.102:11211}" 14 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | failover 2 | tcp pool 3 | thread 4 | 5 | tcproxy "any:11212\ 6 | <-> rr{\ 7 | 192.168.0.100:11211 -> 192.168.0.100:11212\ 8 | 192.168.0.101:11211\ 9 | 192.168.0.102:11211}\ 10 | -> 192.168.0.103:11211" 11 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | PROGNAME = tcproxy 2 | 3 | OBJS = tcproxy.o ae.o util.o policy.o zmalloc.o anet.o 4 | 5 | CFLAGS_GEN = -Wall -Werror -g $(CFLAGS) 6 | CFLAGS_DBG = -ggdb $(CFLAGS_GEN) 7 | CFLAGS_OPT = -O3 -Wno-format $(CFLAGS_GEN) 8 | DEBUG ?= 9 | 10 | CCCOLOR="\033[34m" 11 | LINKCOLOR="\033[34;1m" 12 | SRCCOLOR="\033[33m" 13 | BINCOLOR="\033[37;1m" 14 | MAKECOLOR="\033[32;1m" 15 | ENDCOLOR="\033[0m" 16 | 17 | QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR); 18 | QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR); 19 | 20 | LDFLAGS += 21 | LIBS += 22 | 23 | all: $(PROGNAME) 24 | 25 | %.o: %.c 26 | $(QUIET_CC)$(CC) -c $(CFLAGS) $(CFLAGS_OPT) $(DEBUG) $(COMPILE_TIME) $< 27 | 28 | $(PROGNAME): $(OBJS) 29 | $(QUIET_LINK)$(CC) -o $(PROGNAME) $(CFLAGS_OPT) $(DEBUG) $(OBJS) $(CCLINK) 30 | @echo 31 | @echo "Make Complete. Read README for how to use." 32 | @echo 33 | @echo "Having problems with it? Send complains and bugs to dccmx@dccmx.com" 34 | @echo 35 | 36 | clean: 37 | rm -f $(PROGNAME) core core.[1-9][0-9]* *.o memcheck.out callgrind.out.[1-9][0-9]* massif.out.[1-9][0-9]* 38 | -------------------------------------------------------------------------------- /src/ae.c: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "ae.h" 42 | #include "zmalloc.h" 43 | #include "config.h" 44 | 45 | /* Include the best multiplexing layer supported by this system. 46 | * The following should be ordered by performances, descending. */ 47 | #ifdef HAVE_EPOLL 48 | #include "ae_epoll.c" 49 | #else 50 | #ifdef HAVE_KQUEUE 51 | #include "ae_kqueue.c" 52 | #else 53 | #include "ae_select.c" 54 | #endif 55 | #endif 56 | 57 | aeEventLoop *aeCreateEventLoop(int setsize) { 58 | aeEventLoop *eventLoop; 59 | int i; 60 | 61 | if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err; 62 | eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize); 63 | eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize); 64 | if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err; 65 | eventLoop->setsize = setsize; 66 | eventLoop->timeEventHead = NULL; 67 | eventLoop->timeEventNextId = 0; 68 | eventLoop->stop = 0; 69 | eventLoop->maxfd = -1; 70 | eventLoop->beforesleep = NULL; 71 | if (aeApiCreate(eventLoop) == -1) goto err; 72 | /* Events with mask == AE_NONE are not set. So let's initialize the 73 | * vector with it. */ 74 | for (i = 0; i < setsize; i++) 75 | eventLoop->events[i].mask = AE_NONE; 76 | return eventLoop; 77 | 78 | err: 79 | if (eventLoop) { 80 | zfree(eventLoop->events); 81 | zfree(eventLoop->fired); 82 | zfree(eventLoop); 83 | } 84 | return NULL; 85 | } 86 | 87 | void aeDeleteEventLoop(aeEventLoop *eventLoop) { 88 | aeApiFree(eventLoop); 89 | zfree(eventLoop->events); 90 | zfree(eventLoop->fired); 91 | zfree(eventLoop); 92 | } 93 | 94 | void aeStop(aeEventLoop *eventLoop) { 95 | eventLoop->stop = 1; 96 | } 97 | 98 | int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 99 | aeFileProc *proc, void *clientData) 100 | { 101 | if (fd >= eventLoop->setsize) return AE_ERR; 102 | aeFileEvent *fe = &eventLoop->events[fd]; 103 | 104 | if (aeApiAddEvent(eventLoop, fd, mask) == -1) 105 | return AE_ERR; 106 | fe->mask |= mask; 107 | if (mask & AE_READABLE) fe->rfileProc = proc; 108 | if (mask & AE_WRITABLE) fe->wfileProc = proc; 109 | fe->clientData = clientData; 110 | if (fd > eventLoop->maxfd) 111 | eventLoop->maxfd = fd; 112 | return AE_OK; 113 | } 114 | 115 | void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) 116 | { 117 | if (fd >= eventLoop->setsize) return; 118 | aeFileEvent *fe = &eventLoop->events[fd]; 119 | 120 | if (fe->mask == AE_NONE) return; 121 | fe->mask = fe->mask & (~mask); 122 | if (fd == eventLoop->maxfd && fe->mask == AE_NONE) { 123 | /* Update the max fd */ 124 | int j; 125 | 126 | for (j = eventLoop->maxfd-1; j >= 0; j--) 127 | if (eventLoop->events[j].mask != AE_NONE) break; 128 | eventLoop->maxfd = j; 129 | } 130 | aeApiDelEvent(eventLoop, fd, mask); 131 | } 132 | 133 | int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { 134 | if (fd >= eventLoop->setsize) return 0; 135 | aeFileEvent *fe = &eventLoop->events[fd]; 136 | 137 | return fe->mask; 138 | } 139 | 140 | static void aeGetTime(long *seconds, long *milliseconds) 141 | { 142 | struct timeval tv; 143 | 144 | gettimeofday(&tv, NULL); 145 | *seconds = tv.tv_sec; 146 | *milliseconds = tv.tv_usec/1000; 147 | } 148 | 149 | static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) { 150 | long cur_sec, cur_ms, when_sec, when_ms; 151 | 152 | aeGetTime(&cur_sec, &cur_ms); 153 | when_sec = cur_sec + milliseconds/1000; 154 | when_ms = cur_ms + milliseconds%1000; 155 | if (when_ms >= 1000) { 156 | when_sec ++; 157 | when_ms -= 1000; 158 | } 159 | *sec = when_sec; 160 | *ms = when_ms; 161 | } 162 | 163 | long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 164 | aeTimeProc *proc, void *clientData, 165 | aeEventFinalizerProc *finalizerProc) 166 | { 167 | long long id = eventLoop->timeEventNextId++; 168 | aeTimeEvent *te; 169 | 170 | te = zmalloc(sizeof(*te)); 171 | if (te == NULL) return AE_ERR; 172 | te->id = id; 173 | aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms); 174 | te->timeProc = proc; 175 | te->finalizerProc = finalizerProc; 176 | te->clientData = clientData; 177 | te->next = eventLoop->timeEventHead; 178 | eventLoop->timeEventHead = te; 179 | return id; 180 | } 181 | 182 | int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) 183 | { 184 | aeTimeEvent *te, *prev = NULL; 185 | 186 | te = eventLoop->timeEventHead; 187 | while(te) { 188 | if (te->id == id) { 189 | if (prev == NULL) 190 | eventLoop->timeEventHead = te->next; 191 | else 192 | prev->next = te->next; 193 | if (te->finalizerProc) 194 | te->finalizerProc(eventLoop, te->clientData); 195 | zfree(te); 196 | return AE_OK; 197 | } 198 | prev = te; 199 | te = te->next; 200 | } 201 | return AE_ERR; /* NO event with the specified ID found */ 202 | } 203 | 204 | /* Search the first timer to fire. 205 | * This operation is useful to know how many time the select can be 206 | * put in sleep without to delay any event. 207 | * If there are no timers NULL is returned. 208 | * 209 | * Note that's O(N) since time events are unsorted. 210 | * Possible optimizations (not needed by Redis so far, but...): 211 | * 1) Insert the event in order, so that the nearest is just the head. 212 | * Much better but still insertion or deletion of timers is O(N). 213 | * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)). 214 | */ 215 | static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop) 216 | { 217 | aeTimeEvent *te = eventLoop->timeEventHead; 218 | aeTimeEvent *nearest = NULL; 219 | 220 | while(te) { 221 | if (!nearest || te->when_sec < nearest->when_sec || 222 | (te->when_sec == nearest->when_sec && 223 | te->when_ms < nearest->when_ms)) 224 | nearest = te; 225 | te = te->next; 226 | } 227 | return nearest; 228 | } 229 | 230 | /* Process time events */ 231 | static int processTimeEvents(aeEventLoop *eventLoop) { 232 | int processed = 0; 233 | aeTimeEvent *te; 234 | long long maxId; 235 | 236 | te = eventLoop->timeEventHead; 237 | maxId = eventLoop->timeEventNextId-1; 238 | while(te) { 239 | long now_sec, now_ms; 240 | long long id; 241 | 242 | if (te->id > maxId) { 243 | te = te->next; 244 | continue; 245 | } 246 | aeGetTime(&now_sec, &now_ms); 247 | if (now_sec > te->when_sec || 248 | (now_sec == te->when_sec && now_ms >= te->when_ms)) 249 | { 250 | int retval; 251 | 252 | id = te->id; 253 | retval = te->timeProc(eventLoop, id, te->clientData); 254 | processed++; 255 | /* After an event is processed our time event list may 256 | * no longer be the same, so we restart from head. 257 | * Still we make sure to don't process events registered 258 | * by event handlers itself in order to don't loop forever. 259 | * To do so we saved the max ID we want to handle. 260 | * 261 | * FUTURE OPTIMIZATIONS: 262 | * Note that this is NOT great algorithmically. Redis uses 263 | * a single time event so it's not a problem but the right 264 | * way to do this is to add the new elements on head, and 265 | * to flag deleted elements in a special way for later 266 | * deletion (putting references to the nodes to delete into 267 | * another linked list). */ 268 | if (retval != AE_NOMORE) { 269 | aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); 270 | } else { 271 | aeDeleteTimeEvent(eventLoop, id); 272 | } 273 | te = eventLoop->timeEventHead; 274 | } else { 275 | te = te->next; 276 | } 277 | } 278 | return processed; 279 | } 280 | 281 | /* Process every pending time event, then every pending file event 282 | * (that may be registered by time event callbacks just processed). 283 | * Without special flags the function sleeps until some file event 284 | * fires, or when the next time event occurrs (if any). 285 | * 286 | * If flags is 0, the function does nothing and returns. 287 | * if flags has AE_ALL_EVENTS set, all the kind of events are processed. 288 | * if flags has AE_FILE_EVENTS set, file events are processed. 289 | * if flags has AE_TIME_EVENTS set, time events are processed. 290 | * if flags has AE_DONT_WAIT set the function returns ASAP until all 291 | * the events that's possible to process without to wait are processed. 292 | * 293 | * The function returns the number of events processed. */ 294 | int aeProcessEvents(aeEventLoop *eventLoop, int flags) 295 | { 296 | int processed = 0, numevents; 297 | 298 | /* Nothing to do? return ASAP */ 299 | if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0; 300 | 301 | /* Note that we want call select() even if there are no 302 | * file events to process as long as we want to process time 303 | * events, in order to sleep until the next time event is ready 304 | * to fire. */ 305 | if (eventLoop->maxfd != -1 || 306 | ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { 307 | int j; 308 | aeTimeEvent *shortest = NULL; 309 | struct timeval tv, *tvp; 310 | 311 | if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) 312 | shortest = aeSearchNearestTimer(eventLoop); 313 | if (shortest) { 314 | long now_sec, now_ms; 315 | 316 | /* Calculate the time missing for the nearest 317 | * timer to fire. */ 318 | aeGetTime(&now_sec, &now_ms); 319 | tvp = &tv; 320 | tvp->tv_sec = shortest->when_sec - now_sec; 321 | if (shortest->when_ms < now_ms) { 322 | tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000; 323 | tvp->tv_sec --; 324 | } else { 325 | tvp->tv_usec = (shortest->when_ms - now_ms)*1000; 326 | } 327 | if (tvp->tv_sec < 0) tvp->tv_sec = 0; 328 | if (tvp->tv_usec < 0) tvp->tv_usec = 0; 329 | } else { 330 | /* If we have to check for events but need to return 331 | * ASAP because of AE_DONT_WAIT we need to se the timeout 332 | * to zero */ 333 | if (flags & AE_DONT_WAIT) { 334 | tv.tv_sec = tv.tv_usec = 0; 335 | tvp = &tv; 336 | } else { 337 | /* Otherwise we can block */ 338 | tvp = NULL; /* wait forever */ 339 | } 340 | } 341 | 342 | numevents = aeApiPoll(eventLoop, tvp); 343 | for (j = 0; j < numevents; j++) { 344 | aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; 345 | int mask = eventLoop->fired[j].mask; 346 | int fd = eventLoop->fired[j].fd; 347 | int rfired = 0; 348 | 349 | /* note the fe->mask & mask & ... code: maybe an already processed 350 | * event removed an element that fired and we still didn't 351 | * processed, so we check if the event is still valid. */ 352 | if (fe->mask & mask & AE_READABLE) { 353 | rfired = 1; 354 | fe->rfileProc(eventLoop,fd,fe->clientData,mask); 355 | } 356 | if (fe->mask & mask & AE_WRITABLE) { 357 | if (!rfired || fe->wfileProc != fe->rfileProc) 358 | fe->wfileProc(eventLoop,fd,fe->clientData,mask); 359 | } 360 | processed++; 361 | } 362 | } 363 | /* Check time events */ 364 | if (flags & AE_TIME_EVENTS) 365 | processed += processTimeEvents(eventLoop); 366 | 367 | return processed; /* return the number of processed file/time events */ 368 | } 369 | 370 | /* Wait for millseconds until the given file descriptor becomes 371 | * writable/readable/exception */ 372 | int aeWait(int fd, int mask, long long milliseconds) { 373 | struct pollfd pfd; 374 | int retmask = 0, retval; 375 | 376 | memset(&pfd, 0, sizeof(pfd)); 377 | pfd.fd = fd; 378 | if (mask & AE_READABLE) pfd.events |= POLLIN; 379 | if (mask & AE_WRITABLE) pfd.events |= POLLOUT; 380 | 381 | if ((retval = poll(&pfd, 1, milliseconds))== 1) { 382 | if (pfd.revents & POLLIN) retmask |= AE_READABLE; 383 | if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE; 384 | return retmask; 385 | } else { 386 | return retval; 387 | } 388 | } 389 | 390 | void aeMain(aeEventLoop *eventLoop) { 391 | eventLoop->stop = 0; 392 | while (!eventLoop->stop) { 393 | if (eventLoop->beforesleep != NULL) 394 | eventLoop->beforesleep(eventLoop); 395 | aeProcessEvents(eventLoop, AE_ALL_EVENTS); 396 | } 397 | } 398 | 399 | char *aeGetApiName(void) { 400 | return aeApiName(); 401 | } 402 | 403 | void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) { 404 | eventLoop->beforesleep = beforesleep; 405 | } 406 | -------------------------------------------------------------------------------- /src/ae.h: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __AE_H__ 34 | #define __AE_H__ 35 | 36 | #define AE_OK 0 37 | #define AE_ERR -1 38 | 39 | #define AE_NONE 0 40 | #define AE_READABLE 1 41 | #define AE_WRITABLE 2 42 | 43 | #define AE_FILE_EVENTS 1 44 | #define AE_TIME_EVENTS 2 45 | #define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS) 46 | #define AE_DONT_WAIT 4 47 | 48 | #define AE_NOMORE -1 49 | 50 | /* Macros */ 51 | #define AE_NOTUSED(V) ((void) V) 52 | 53 | struct aeEventLoop; 54 | 55 | /* Types and data structures */ 56 | typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask); 57 | typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData); 58 | typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData); 59 | typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop); 60 | 61 | /* File event structure */ 62 | typedef struct aeFileEvent { 63 | int mask; /* one of AE_(READABLE|WRITABLE) */ 64 | aeFileProc *rfileProc; 65 | aeFileProc *wfileProc; 66 | void *clientData; 67 | } aeFileEvent; 68 | 69 | /* Time event structure */ 70 | typedef struct aeTimeEvent { 71 | long long id; /* time event identifier. */ 72 | long when_sec; /* seconds */ 73 | long when_ms; /* milliseconds */ 74 | aeTimeProc *timeProc; 75 | aeEventFinalizerProc *finalizerProc; 76 | void *clientData; 77 | struct aeTimeEvent *next; 78 | } aeTimeEvent; 79 | 80 | /* A fired event */ 81 | typedef struct aeFiredEvent { 82 | int fd; 83 | int mask; 84 | } aeFiredEvent; 85 | 86 | /* State of an event based program */ 87 | typedef struct aeEventLoop { 88 | int maxfd; /* highest file descriptor currently registered */ 89 | int setsize; /* max number of file descriptors tracked */ 90 | long long timeEventNextId; 91 | aeFileEvent *events; /* Registered events */ 92 | aeFiredEvent *fired; /* Fired events */ 93 | aeTimeEvent *timeEventHead; 94 | int stop; 95 | void *apidata; /* This is used for polling API specific data */ 96 | aeBeforeSleepProc *beforesleep; 97 | } aeEventLoop; 98 | 99 | /* Prototypes */ 100 | aeEventLoop *aeCreateEventLoop(int setsize); 101 | void aeDeleteEventLoop(aeEventLoop *eventLoop); 102 | void aeStop(aeEventLoop *eventLoop); 103 | int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 104 | aeFileProc *proc, void *clientData); 105 | void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask); 106 | int aeGetFileEvents(aeEventLoop *eventLoop, int fd); 107 | long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 108 | aeTimeProc *proc, void *clientData, 109 | aeEventFinalizerProc *finalizerProc); 110 | int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id); 111 | int aeProcessEvents(aeEventLoop *eventLoop, int flags); 112 | int aeWait(int fd, int mask, long long milliseconds); 113 | void aeMain(aeEventLoop *eventLoop); 114 | char *aeGetApiName(void); 115 | void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep); 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/ae_epoll.c: -------------------------------------------------------------------------------- 1 | /* Linux epoll(2) based ae.c module 2 | * Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com 3 | * Released under the BSD license. See the COPYING file for more info. */ 4 | 5 | #include 6 | 7 | typedef struct aeApiState { 8 | int epfd; 9 | struct epoll_event *events; 10 | } aeApiState; 11 | 12 | static int aeApiCreate(aeEventLoop *eventLoop) { 13 | aeApiState *state = zmalloc(sizeof(aeApiState)); 14 | 15 | if (!state) return -1; 16 | state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize); 17 | if (!state->events) { 18 | zfree(state); 19 | return -1; 20 | } 21 | state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */ 22 | if (state->epfd == -1) { 23 | zfree(state->events); 24 | zfree(state); 25 | return -1; 26 | } 27 | eventLoop->apidata = state; 28 | return 0; 29 | } 30 | 31 | static void aeApiFree(aeEventLoop *eventLoop) { 32 | aeApiState *state = eventLoop->apidata; 33 | 34 | close(state->epfd); 35 | zfree(state->events); 36 | zfree(state); 37 | } 38 | 39 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 40 | aeApiState *state = eventLoop->apidata; 41 | struct epoll_event ee; 42 | /* If the fd was already monitored for some event, we need a MOD 43 | * operation. Otherwise we need an ADD operation. */ 44 | int op = eventLoop->events[fd].mask == AE_NONE ? 45 | EPOLL_CTL_ADD : EPOLL_CTL_MOD; 46 | 47 | ee.events = 0; 48 | mask |= eventLoop->events[fd].mask; /* Merge old events */ 49 | if (mask & AE_READABLE) ee.events |= EPOLLIN; 50 | if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; 51 | ee.data.u64 = 0; /* avoid valgrind warning */ 52 | ee.data.fd = fd; 53 | if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; 54 | return 0; 55 | } 56 | 57 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { 58 | aeApiState *state = eventLoop->apidata; 59 | struct epoll_event ee; 60 | int mask = eventLoop->events[fd].mask & (~delmask); 61 | 62 | ee.events = 0; 63 | if (mask & AE_READABLE) ee.events |= EPOLLIN; 64 | if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; 65 | ee.data.u64 = 0; /* avoid valgrind warning */ 66 | ee.data.fd = fd; 67 | if (mask != AE_NONE) { 68 | epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee); 69 | } else { 70 | /* Note, Kernel < 2.6.9 requires a non null event pointer even for 71 | * EPOLL_CTL_DEL. */ 72 | epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee); 73 | } 74 | } 75 | 76 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 77 | aeApiState *state = eventLoop->apidata; 78 | int retval, numevents = 0; 79 | 80 | retval = epoll_wait(state->epfd,state->events,eventLoop->setsize, 81 | tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); 82 | if (retval > 0) { 83 | int j; 84 | 85 | numevents = retval; 86 | for (j = 0; j < numevents; j++) { 87 | int mask = 0; 88 | struct epoll_event *e = state->events+j; 89 | 90 | if (e->events & EPOLLIN) mask |= AE_READABLE; 91 | if (e->events & EPOLLOUT) mask |= AE_WRITABLE; 92 | eventLoop->fired[j].fd = e->data.fd; 93 | eventLoop->fired[j].mask = mask; 94 | } 95 | } 96 | return numevents; 97 | } 98 | 99 | static char *aeApiName(void) { 100 | return "epoll"; 101 | } 102 | -------------------------------------------------------------------------------- /src/ae_kqueue.c: -------------------------------------------------------------------------------- 1 | /* Kqueue(2)-based ae.c module 2 | * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com 3 | * Released under the BSD license. See the COPYING file for more info. */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct aeApiState { 10 | int kqfd; 11 | struct kevent *events; 12 | } aeApiState; 13 | 14 | static int aeApiCreate(aeEventLoop *eventLoop) { 15 | aeApiState *state = zmalloc(sizeof(aeApiState)); 16 | 17 | if (!state) return -1; 18 | state->events = zmalloc(sizeof(struct kevent)*eventLoop->setsize); 19 | if (!state->events) { 20 | zfree(state); 21 | return -1; 22 | } 23 | state->kqfd = kqueue(); 24 | if (state->kqfd == -1) { 25 | zfree(state->events); 26 | zfree(state); 27 | return -1; 28 | } 29 | eventLoop->apidata = state; 30 | 31 | return 0; 32 | } 33 | 34 | static void aeApiFree(aeEventLoop *eventLoop) { 35 | aeApiState *state = eventLoop->apidata; 36 | 37 | close(state->kqfd); 38 | zfree(state->events); 39 | zfree(state); 40 | } 41 | 42 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 43 | aeApiState *state = eventLoop->apidata; 44 | struct kevent ke; 45 | 46 | if (mask & AE_READABLE) { 47 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 48 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 49 | } 50 | if (mask & AE_WRITABLE) { 51 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 52 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 53 | } 54 | return 0; 55 | } 56 | 57 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 58 | aeApiState *state = eventLoop->apidata; 59 | struct kevent ke; 60 | 61 | if (mask & AE_READABLE) { 62 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 63 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 64 | } 65 | if (mask & AE_WRITABLE) { 66 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 67 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 68 | } 69 | } 70 | 71 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 72 | aeApiState *state = eventLoop->apidata; 73 | int retval, numevents = 0; 74 | 75 | if (tvp != NULL) { 76 | struct timespec timeout; 77 | timeout.tv_sec = tvp->tv_sec; 78 | timeout.tv_nsec = tvp->tv_usec * 1000; 79 | retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 80 | &timeout); 81 | } else { 82 | retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 83 | NULL); 84 | } 85 | 86 | if (retval > 0) { 87 | int j; 88 | 89 | numevents = retval; 90 | for(j = 0; j < numevents; j++) { 91 | int mask = 0; 92 | struct kevent *e = state->events+j; 93 | 94 | if (e->filter == EVFILT_READ) mask |= AE_READABLE; 95 | if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE; 96 | eventLoop->fired[j].fd = e->ident; 97 | eventLoop->fired[j].mask = mask; 98 | } 99 | } 100 | return numevents; 101 | } 102 | 103 | static char *aeApiName(void) { 104 | return "kqueue"; 105 | } 106 | -------------------------------------------------------------------------------- /src/ae_select.c: -------------------------------------------------------------------------------- 1 | /* Select()-based ae.c module 2 | * Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com 3 | * Released under the BSD license. See the COPYING file for more info. */ 4 | 5 | #include 6 | 7 | typedef struct aeApiState { 8 | fd_set rfds, wfds; 9 | /* We need to have a copy of the fd sets as it's not safe to reuse 10 | * FD sets after select(). */ 11 | fd_set _rfds, _wfds; 12 | } aeApiState; 13 | 14 | static int aeApiCreate(aeEventLoop *eventLoop) { 15 | aeApiState *state = zmalloc(sizeof(aeApiState)); 16 | 17 | if (!state) return -1; 18 | FD_ZERO(&state->rfds); 19 | FD_ZERO(&state->wfds); 20 | eventLoop->apidata = state; 21 | return 0; 22 | } 23 | 24 | static void aeApiFree(aeEventLoop *eventLoop) { 25 | zfree(eventLoop->apidata); 26 | } 27 | 28 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 29 | aeApiState *state = eventLoop->apidata; 30 | 31 | if (mask & AE_READABLE) FD_SET(fd,&state->rfds); 32 | if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds); 33 | return 0; 34 | } 35 | 36 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 37 | aeApiState *state = eventLoop->apidata; 38 | 39 | if (mask & AE_READABLE) FD_CLR(fd,&state->rfds); 40 | if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds); 41 | } 42 | 43 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 44 | aeApiState *state = eventLoop->apidata; 45 | int retval, j, numevents = 0; 46 | 47 | memcpy(&state->_rfds,&state->rfds,sizeof(fd_set)); 48 | memcpy(&state->_wfds,&state->wfds,sizeof(fd_set)); 49 | 50 | retval = select(eventLoop->maxfd+1, 51 | &state->_rfds,&state->_wfds,NULL,tvp); 52 | if (retval > 0) { 53 | for (j = 0; j <= eventLoop->maxfd; j++) { 54 | int mask = 0; 55 | aeFileEvent *fe = &eventLoop->events[j]; 56 | 57 | if (fe->mask == AE_NONE) continue; 58 | if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds)) 59 | mask |= AE_READABLE; 60 | if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds)) 61 | mask |= AE_WRITABLE; 62 | eventLoop->fired[numevents].fd = j; 63 | eventLoop->fired[numevents].mask = mask; 64 | numevents++; 65 | } 66 | } 67 | return numevents; 68 | } 69 | 70 | static char *aeApiName(void) { 71 | return "select"; 72 | } 73 | -------------------------------------------------------------------------------- /src/anet.c: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include "fmacros.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "anet.h" 49 | 50 | static void anetSetError(char *err, const char *fmt, ...) 51 | { 52 | va_list ap; 53 | 54 | if (!err) return; 55 | va_start(ap, fmt); 56 | vsnprintf(err, ANET_ERR_LEN, fmt, ap); 57 | va_end(ap); 58 | } 59 | 60 | int anetNonBlock(char *err, int fd) 61 | { 62 | int flags; 63 | 64 | /* Set the socket nonblocking. 65 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be 66 | * interrupted by a signal. */ 67 | if ((flags = fcntl(fd, F_GETFL)) == -1) { 68 | anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); 69 | return ANET_ERR; 70 | } 71 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { 72 | anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); 73 | return ANET_ERR; 74 | } 75 | return ANET_OK; 76 | } 77 | 78 | int anetTcpNoDelay(char *err, int fd) 79 | { 80 | int yes = 1; 81 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) 82 | { 83 | anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); 84 | return ANET_ERR; 85 | } 86 | return ANET_OK; 87 | } 88 | 89 | int anetSetSendBuffer(char *err, int fd, int buffsize) 90 | { 91 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) 92 | { 93 | anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); 94 | return ANET_ERR; 95 | } 96 | return ANET_OK; 97 | } 98 | 99 | int anetTcpKeepAlive(char *err, int fd) 100 | { 101 | int yes = 1; 102 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) { 103 | anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); 104 | return ANET_ERR; 105 | } 106 | return ANET_OK; 107 | } 108 | 109 | int anetResolve(char *err, char *host, char *ipbuf) 110 | { 111 | struct sockaddr_in sa; 112 | 113 | sa.sin_family = AF_INET; 114 | if (inet_aton(host, &sa.sin_addr) == 0) { 115 | struct hostent *he; 116 | 117 | he = gethostbyname(host); 118 | if (he == NULL) { 119 | anetSetError(err, "can't resolve: %s", host); 120 | return ANET_ERR; 121 | } 122 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 123 | } 124 | strcpy(ipbuf,inet_ntoa(sa.sin_addr)); 125 | return ANET_OK; 126 | } 127 | 128 | static int anetCreateSocket(char *err, int domain) { 129 | int s, on = 1; 130 | if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { 131 | anetSetError(err, "creating socket: %s", strerror(errno)); 132 | return ANET_ERR; 133 | } 134 | 135 | /* Make sure connection-intensive things like the redis benckmark 136 | * will be able to close/open sockets a zillion of times */ 137 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { 138 | anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); 139 | return ANET_ERR; 140 | } 141 | return s; 142 | } 143 | 144 | #define ANET_CONNECT_NONE 0 145 | #define ANET_CONNECT_NONBLOCK 1 146 | static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) 147 | { 148 | int s; 149 | struct sockaddr_in sa; 150 | 151 | if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) 152 | return ANET_ERR; 153 | 154 | sa.sin_family = AF_INET; 155 | sa.sin_port = htons(port); 156 | if (inet_aton(addr, &sa.sin_addr) == 0) { 157 | struct hostent *he; 158 | 159 | he = gethostbyname(addr); 160 | if (he == NULL) { 161 | anetSetError(err, "can't resolve: %s", addr); 162 | close(s); 163 | return ANET_ERR; 164 | } 165 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 166 | } 167 | if (flags & ANET_CONNECT_NONBLOCK) { 168 | if (anetNonBlock(err,s) != ANET_OK) 169 | return ANET_ERR; 170 | } 171 | if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { 172 | if (errno == EINPROGRESS && 173 | flags & ANET_CONNECT_NONBLOCK) 174 | return s; 175 | 176 | anetSetError(err, "connect: %s", strerror(errno)); 177 | close(s); 178 | return ANET_ERR; 179 | } 180 | return s; 181 | } 182 | 183 | int anetTcpConnect(char *err, char *addr, int port) 184 | { 185 | return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE); 186 | } 187 | 188 | int anetTcpNonBlockConnect(char *err, char *addr, int port) 189 | { 190 | return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK); 191 | } 192 | 193 | int anetUnixGenericConnect(char *err, char *path, int flags) 194 | { 195 | int s; 196 | struct sockaddr_un sa; 197 | 198 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 199 | return ANET_ERR; 200 | 201 | sa.sun_family = AF_LOCAL; 202 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 203 | if (flags & ANET_CONNECT_NONBLOCK) { 204 | if (anetNonBlock(err,s) != ANET_OK) 205 | return ANET_ERR; 206 | } 207 | if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { 208 | if (errno == EINPROGRESS && 209 | flags & ANET_CONNECT_NONBLOCK) 210 | return s; 211 | 212 | anetSetError(err, "connect: %s", strerror(errno)); 213 | close(s); 214 | return ANET_ERR; 215 | } 216 | return s; 217 | } 218 | 219 | int anetUnixConnect(char *err, char *path) 220 | { 221 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE); 222 | } 223 | 224 | int anetUnixNonBlockConnect(char *err, char *path) 225 | { 226 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK); 227 | } 228 | 229 | /* Like read(2) but make sure 'count' is read before to return 230 | * (unless error or EOF condition is encountered) */ 231 | int anetRead(int fd, char *buf, int count) 232 | { 233 | int nread, totlen = 0; 234 | while(totlen != count) { 235 | nread = read(fd,buf,count-totlen); 236 | if (nread == 0) return totlen; 237 | if (nread == -1) return -1; 238 | totlen += nread; 239 | buf += nread; 240 | } 241 | return totlen; 242 | } 243 | 244 | /* Like write(2) but make sure 'count' is read before to return 245 | * (unless error is encountered) */ 246 | int anetWrite(int fd, char *buf, int count) 247 | { 248 | int nwritten, totlen = 0; 249 | while(totlen != count) { 250 | nwritten = write(fd,buf,count-totlen); 251 | if (nwritten == 0) return totlen; 252 | if (nwritten == -1) return -1; 253 | totlen += nwritten; 254 | buf += nwritten; 255 | } 256 | return totlen; 257 | } 258 | 259 | static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { 260 | if (bind(s,sa,len) == -1) { 261 | anetSetError(err, "bind: %s", strerror(errno)); 262 | close(s); 263 | return ANET_ERR; 264 | } 265 | 266 | /* Use a backlog of 512 entries. We pass 511 to the listen() call because 267 | * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); 268 | * which will thus give us a backlog of 512 entries */ 269 | if (listen(s, 511) == -1) { 270 | anetSetError(err, "listen: %s", strerror(errno)); 271 | close(s); 272 | return ANET_ERR; 273 | } 274 | return ANET_OK; 275 | } 276 | 277 | int anetTcpServer(char *err, int port, char *bindaddr) 278 | { 279 | int s; 280 | struct sockaddr_in sa; 281 | 282 | if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) 283 | return ANET_ERR; 284 | 285 | memset(&sa,0,sizeof(sa)); 286 | sa.sin_family = AF_INET; 287 | sa.sin_port = htons(port); 288 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 289 | if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) { 290 | anetSetError(err, "invalid bind address"); 291 | close(s); 292 | return ANET_ERR; 293 | } 294 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) 295 | return ANET_ERR; 296 | return s; 297 | } 298 | 299 | int anetUnixServer(char *err, char *path, mode_t perm) 300 | { 301 | int s; 302 | struct sockaddr_un sa; 303 | 304 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 305 | return ANET_ERR; 306 | 307 | memset(&sa,0,sizeof(sa)); 308 | sa.sun_family = AF_LOCAL; 309 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 310 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) 311 | return ANET_ERR; 312 | if (perm) 313 | chmod(sa.sun_path, perm); 314 | return s; 315 | } 316 | 317 | static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { 318 | int fd; 319 | while(1) { 320 | fd = accept(s,sa,len); 321 | if (fd == -1) { 322 | if (errno == EINTR) 323 | continue; 324 | else { 325 | anetSetError(err, "accept: %s", strerror(errno)); 326 | return ANET_ERR; 327 | } 328 | } 329 | break; 330 | } 331 | return fd; 332 | } 333 | 334 | int anetTcpAccept(char *err, int s, char *ip, int *port) { 335 | int fd; 336 | struct sockaddr_in sa; 337 | socklen_t salen = sizeof(sa); 338 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) 339 | return ANET_ERR; 340 | 341 | if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); 342 | if (port) *port = ntohs(sa.sin_port); 343 | return fd; 344 | } 345 | 346 | int anetUnixAccept(char *err, int s) { 347 | int fd; 348 | struct sockaddr_un sa; 349 | socklen_t salen = sizeof(sa); 350 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) 351 | return ANET_ERR; 352 | 353 | return fd; 354 | } 355 | 356 | int anetPeerToString(int fd, char *ip, int *port) { 357 | struct sockaddr_in sa; 358 | socklen_t salen = sizeof(sa); 359 | 360 | if (getpeername(fd,(struct sockaddr*)&sa,&salen) == -1) { 361 | *port = 0; 362 | ip[0] = '?'; 363 | ip[1] = '\0'; 364 | return -1; 365 | } 366 | if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); 367 | if (port) *port = ntohs(sa.sin_port); 368 | return 0; 369 | } 370 | -------------------------------------------------------------------------------- /src/anet.h: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef ANET_H 32 | #define ANET_H 33 | 34 | #define ANET_OK 0 35 | #define ANET_ERR -1 36 | #define ANET_ERR_LEN 256 37 | 38 | #if defined(__sun) 39 | #define AF_LOCAL AF_UNIX 40 | #endif 41 | 42 | int anetTcpConnect(char *err, char *addr, int port); 43 | int anetTcpNonBlockConnect(char *err, char *addr, int port); 44 | int anetUnixConnect(char *err, char *path); 45 | int anetUnixNonBlockConnect(char *err, char *path); 46 | int anetRead(int fd, char *buf, int count); 47 | int anetResolve(char *err, char *host, char *ipbuf); 48 | int anetTcpServer(char *err, int port, char *bindaddr); 49 | int anetUnixServer(char *err, char *path, mode_t perm); 50 | int anetTcpAccept(char *err, int serversock, char *ip, int *port); 51 | int anetUnixAccept(char *err, int serversock); 52 | int anetWrite(int fd, char *buf, int count); 53 | int anetNonBlock(char *err, int fd); 54 | int anetTcpNoDelay(char *err, int fd); 55 | int anetTcpKeepAlive(char *err, int fd); 56 | int anetPeerToString(int fd, char *ip, int *port); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H 2 | #define __CONFIG_H 3 | 4 | #ifdef __APPLE__ 5 | #include 6 | #endif 7 | 8 | /* Test for proc filesystem */ 9 | #ifdef __linux__ 10 | #define HAVE_PROCFS 1 11 | #endif 12 | 13 | /* Test for task_info() */ 14 | #if defined(__APPLE__) 15 | #define HAVE_TASKINFO 1 16 | #endif 17 | 18 | /* Test for backtrace() */ 19 | #if defined(__APPLE__) || defined(__linux__) 20 | #define HAVE_BACKTRACE 1 21 | #endif 22 | 23 | /* Test for polling API */ 24 | #ifdef __linux__ 25 | #define HAVE_EPOLL 1 26 | #endif 27 | 28 | #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__) 29 | #define HAVE_KQUEUE 1 30 | #endif 31 | 32 | /* Define aof_fsync to fdatasync() in Linux and fsync() for all the rest */ 33 | #ifdef __linux__ 34 | #define aof_fsync fdatasync 35 | #else 36 | #define aof_fsync fsync 37 | #endif 38 | 39 | /* Byte ordering detection */ 40 | #include /* This will likely define BYTE_ORDER */ 41 | 42 | #ifndef BYTE_ORDER 43 | #if (BSD >= 199103) 44 | # include 45 | #else 46 | #if defined(linux) || defined(__linux__) 47 | # include 48 | #else 49 | #define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */ 50 | #define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */ 51 | #define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/ 52 | 53 | #if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \ 54 | defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \ 55 | defined(__alpha__) || defined(__alpha) 56 | #define BYTE_ORDER LITTLE_ENDIAN 57 | #endif 58 | 59 | #if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \ 60 | defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \ 61 | defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\ 62 | defined(apollo) || defined(__convex__) || defined(_CRAY) || \ 63 | defined(__hppa) || defined(__hp9000) || \ 64 | defined(__hp9000s300) || defined(__hp9000s700) || \ 65 | defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc) 66 | #define BYTE_ORDER BIG_ENDIAN 67 | #endif 68 | #endif /* linux */ 69 | #endif /* BSD */ 70 | #endif /* BYTE_ORDER */ 71 | 72 | #if defined(__BYTE_ORDER) && !defined(BYTE_ORDER) 73 | #if (__BYTE_ORDER == __LITTLE_ENDIAN) 74 | #define BYTE_ORDER LITTLE_ENDIAN 75 | #else 76 | #define BYTE_ORDER BIG_ENDIAN 77 | #endif 78 | #endif 79 | 80 | #if !defined(BYTE_ORDER) || \ 81 | (BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN) 82 | /* you must determine what the correct bit order is for 83 | * your compiler - the next line is an intentional error 84 | * which will force your compiles to bomb until you fix 85 | * the above macros. 86 | */ 87 | #error "Undefined or invalid BYTE_ORDER" 88 | #endif 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/fmacros.h: -------------------------------------------------------------------------------- 1 | #ifndef _REDIS_FMACRO_H 2 | #define _REDIS_FMACRO_H 3 | 4 | #define _BSD_SOURCE 5 | 6 | #if defined(__linux__) || defined(__OpenBSD__) 7 | #define _XOPEN_SOURCE 700 8 | #else 9 | #define _XOPEN_SOURCE 10 | #endif 11 | 12 | #define _LARGEFILE_SOURCE 13 | #define _FILE_OFFSET_BITS 64 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/policy.c: -------------------------------------------------------------------------------- 1 | 2 | #line 1 "policy.rl" 3 | #include 4 | #include 5 | #include 6 | 7 | #include "policy.h" 8 | 9 | static Hostent host; 10 | static int addr_p; 11 | static int have_addr; 12 | 13 | 14 | #line 92 "policy.rl" 15 | 16 | 17 | 18 | #line 19 "policy.c" 19 | static const char _policy_parser_actions[] = { 20 | 0, 1, 3, 1, 4, 1, 6, 1, 21 | 7, 1, 8, 1, 9, 1, 10, 2, 22 | 0, 3, 2, 2, 4, 2, 3, 4, 23 | 2, 5, 1, 3, 8, 0, 3, 4, 24 | 0, 3, 2, 4, 5, 8, 0, 3, 25 | 2, 4 26 | }; 27 | 28 | static const unsigned char _policy_parser_key_offsets[] = { 29 | 0, 0, 4, 9, 11, 12, 19, 21, 30 | 24, 26, 29, 31, 34, 37, 38, 40, 31 | 43, 44, 47, 48, 49, 50, 51, 52, 32 | 53, 55, 57, 62, 67, 73, 74, 75, 33 | 76, 78, 82, 86, 90, 94, 96, 97, 34 | 98, 99, 100, 101, 102, 103, 104, 106, 35 | 109, 111, 114, 116, 119, 122, 125, 126, 36 | 129, 130, 135, 140, 141, 142, 143, 144, 37 | 145, 146, 147, 148, 149, 151, 153, 156, 38 | 158, 161, 163, 166, 169, 170, 172, 176, 39 | 180, 184, 188, 190, 193, 194, 197, 198, 40 | 203, 208, 209, 210, 211, 212, 213, 214, 41 | 215, 216, 217, 218, 221, 223, 225, 227, 42 | 229, 229, 232, 235 43 | }; 44 | 45 | static const char _policy_parser_trans_keys[] = { 46 | 97, 108, 48, 57, 32, 45, 46, 48, 47 | 57, 32, 45, 62, 32, 97, 104, 108, 48 | 114, 48, 57, 48, 57, 46, 48, 57, 49 | 48, 57, 46, 48, 57, 48, 57, 58, 50 | 48, 57, 58, 48, 57, 58, 48, 57, 51 | 46, 48, 57, 46, 46, 48, 57, 46, 52 | 110, 121, 97, 115, 104, 32, 123, 32, 53 | 123, 32, 97, 108, 48, 57, 32, 46, 54 | 125, 48, 57, 32, 97, 108, 125, 48, 55 | 57, 110, 121, 58, 48, 57, 32, 125, 56 | 48, 57, 32, 125, 48, 57, 32, 125, 57 | 48, 57, 32, 125, 48, 57, 32, 125, 58 | 111, 99, 97, 108, 104, 111, 115, 116, 59 | 48, 57, 46, 48, 57, 48, 57, 46, 60 | 48, 57, 48, 57, 58, 48, 57, 58, 61 | 48, 57, 46, 48, 57, 46, 46, 48, 62 | 57, 46, 32, 46, 125, 48, 57, 32, 63 | 46, 125, 48, 57, 111, 99, 97, 108, 64 | 104, 111, 115, 116, 114, 32, 123, 48, 65 | 57, 46, 48, 57, 48, 57, 46, 48, 66 | 57, 48, 57, 58, 48, 57, 58, 48, 67 | 57, 58, 48, 57, 32, 45, 48, 57, 68 | 32, 45, 48, 57, 32, 45, 48, 57, 69 | 32, 45, 48, 57, 32, 45, 46, 48, 70 | 57, 46, 46, 48, 57, 46, 32, 45, 71 | 46, 48, 57, 32, 45, 46, 48, 57, 72 | 110, 121, 111, 99, 97, 108, 104, 111, 73 | 115, 116, 46, 48, 57, 48, 57, 48, 74 | 57, 48, 57, 48, 57, 46, 48, 57, 75 | 46, 48, 57, 0 76 | }; 77 | 78 | static const char _policy_parser_single_lengths[] = { 79 | 0, 2, 3, 2, 1, 5, 0, 1, 80 | 0, 1, 0, 1, 1, 1, 0, 1, 81 | 1, 1, 1, 1, 1, 1, 1, 1, 82 | 2, 2, 3, 3, 4, 1, 1, 1, 83 | 0, 2, 2, 2, 2, 2, 1, 1, 84 | 1, 1, 1, 1, 1, 1, 0, 1, 85 | 0, 1, 0, 1, 1, 1, 1, 1, 86 | 1, 3, 3, 1, 1, 1, 1, 1, 87 | 1, 1, 1, 1, 2, 0, 1, 0, 88 | 1, 0, 1, 1, 1, 0, 2, 2, 89 | 2, 2, 2, 1, 1, 1, 1, 3, 90 | 3, 1, 1, 1, 1, 1, 1, 1, 91 | 1, 1, 1, 1, 0, 0, 0, 0, 92 | 0, 1, 1, 0 93 | }; 94 | 95 | static const char _policy_parser_range_lengths[] = { 96 | 0, 1, 1, 0, 0, 1, 1, 1, 97 | 1, 1, 1, 1, 1, 0, 1, 1, 98 | 0, 1, 0, 0, 0, 0, 0, 0, 99 | 0, 0, 1, 1, 1, 0, 0, 0, 100 | 1, 1, 1, 1, 1, 0, 0, 0, 101 | 0, 0, 0, 0, 0, 0, 1, 1, 102 | 1, 1, 1, 1, 1, 1, 0, 1, 103 | 0, 1, 1, 0, 0, 0, 0, 0, 104 | 0, 0, 0, 0, 0, 1, 1, 1, 105 | 1, 1, 1, 1, 0, 1, 1, 1, 106 | 1, 1, 0, 1, 0, 1, 0, 1, 107 | 1, 0, 0, 0, 0, 0, 0, 0, 108 | 0, 0, 0, 1, 1, 1, 1, 1, 109 | 0, 1, 1, 0 110 | }; 111 | 112 | static const short _policy_parser_index_offsets[] = { 113 | 0, 0, 4, 9, 12, 14, 21, 23, 114 | 26, 28, 31, 33, 36, 39, 41, 43, 115 | 46, 48, 51, 53, 55, 57, 59, 61, 116 | 63, 66, 69, 74, 79, 85, 87, 89, 117 | 91, 93, 97, 101, 105, 109, 112, 114, 118 | 116, 118, 120, 122, 124, 126, 128, 130, 119 | 133, 135, 138, 140, 143, 146, 149, 151, 120 | 154, 156, 161, 166, 168, 170, 172, 174, 121 | 176, 178, 180, 182, 184, 187, 189, 192, 122 | 194, 197, 199, 202, 205, 207, 209, 213, 123 | 217, 221, 225, 228, 231, 233, 236, 238, 124 | 243, 248, 250, 252, 254, 256, 258, 260, 125 | 262, 264, 266, 268, 271, 273, 275, 277, 126 | 279, 280, 283, 286 127 | }; 128 | 129 | static const char _policy_parser_indicies[] = { 130 | 2, 3, 1, 0, 4, 5, 6, 7, 131 | 0, 8, 9, 0, 10, 0, 10, 12, 132 | 13, 14, 15, 11, 0, 16, 0, 17, 133 | 18, 0, 19, 0, 20, 21, 0, 22, 134 | 0, 24, 23, 0, 24, 25, 0, 24, 135 | 0, 26, 0, 20, 27, 0, 20, 0, 136 | 17, 28, 0, 17, 0, 29, 0, 25, 137 | 0, 30, 0, 31, 0, 32, 0, 33, 138 | 34, 0, 35, 36, 0, 36, 38, 39, 139 | 37, 0, 40, 41, 43, 42, 0, 44, 140 | 38, 39, 43, 37, 0, 45, 0, 46, 141 | 0, 47, 0, 48, 0, 40, 43, 49, 142 | 0, 40, 43, 50, 0, 40, 43, 51, 143 | 0, 40, 43, 52, 0, 40, 43, 0, 144 | 53, 0, 54, 0, 55, 0, 56, 0, 145 | 57, 0, 58, 0, 59, 0, 46, 0, 146 | 60, 0, 61, 62, 0, 63, 0, 64, 147 | 65, 0, 66, 0, 47, 67, 0, 47, 148 | 46, 0, 64, 68, 0, 64, 0, 61, 149 | 69, 0, 61, 0, 40, 41, 43, 70, 150 | 0, 40, 41, 43, 51, 0, 71, 0, 151 | 72, 0, 73, 0, 74, 0, 75, 0, 152 | 76, 0, 77, 0, 25, 0, 78, 0, 153 | 79, 80, 0, 81, 0, 82, 83, 0, 154 | 84, 0, 85, 86, 0, 87, 0, 89, 155 | 88, 0, 89, 90, 0, 89, 0, 91, 156 | 0, 4, 5, 92, 0, 4, 5, 93, 157 | 0, 4, 5, 94, 0, 4, 5, 95, 158 | 0, 4, 5, 0, 85, 96, 0, 85, 159 | 0, 82, 97, 0, 82, 0, 4, 5, 160 | 6, 98, 0, 4, 5, 6, 94, 0, 161 | 99, 0, 90, 0, 100, 0, 101, 0, 162 | 102, 0, 103, 0, 104, 0, 105, 0, 163 | 106, 0, 90, 0, 107, 108, 0, 109, 164 | 0, 110, 0, 111, 0, 112, 0, 0, 165 | 107, 113, 0, 107, 111, 0, 0, 0 166 | }; 167 | 168 | static const char _policy_parser_trans_targs[] = { 169 | 0, 2, 89, 91, 3, 4, 69, 87, 170 | 3, 4, 5, 99, 19, 21, 59, 67, 171 | 7, 8, 17, 9, 10, 15, 11, 12, 172 | 14, 13, 100, 16, 18, 20, 22, 23, 173 | 24, 25, 26, 25, 26, 27, 29, 38, 174 | 28, 46, 57, 107, 28, 30, 31, 32, 175 | 33, 34, 35, 36, 37, 39, 40, 41, 176 | 42, 43, 44, 45, 47, 48, 55, 49, 177 | 50, 53, 51, 52, 54, 56, 58, 60, 178 | 61, 62, 63, 64, 65, 66, 68, 25, 179 | 26, 70, 71, 85, 72, 73, 83, 74, 180 | 75, 77, 76, 78, 79, 80, 81, 82, 181 | 84, 86, 88, 90, 92, 93, 94, 95, 182 | 96, 97, 98, 6, 105, 101, 102, 103, 183 | 104, 106 184 | }; 185 | 186 | static const char _policy_parser_trans_actions[] = { 187 | 13, 31, 15, 15, 5, 5, 1, 21, 188 | 0, 0, 0, 36, 27, 0, 27, 0, 189 | 1, 1, 1, 1, 1, 1, 1, 1, 190 | 24, 1, 18, 1, 1, 1, 0, 0, 191 | 0, 11, 11, 0, 0, 31, 15, 15, 192 | 7, 1, 21, 7, 0, 1, 1, 24, 193 | 18, 3, 3, 3, 3, 1, 1, 1, 194 | 1, 1, 1, 1, 1, 1, 1, 1, 195 | 1, 1, 1, 1, 1, 1, 21, 1, 196 | 1, 1, 1, 1, 1, 1, 0, 9, 197 | 9, 1, 1, 1, 1, 1, 1, 1, 198 | 1, 24, 1, 18, 3, 3, 3, 3, 199 | 1, 1, 21, 1, 1, 1, 1, 1, 200 | 1, 1, 1, 1, 21, 3, 3, 3, 201 | 3, 21 202 | }; 203 | 204 | static const char _policy_parser_eof_actions[] = { 205 | 0, 13, 13, 13, 13, 13, 13, 13, 206 | 13, 13, 13, 13, 13, 13, 13, 13, 207 | 13, 13, 13, 13, 13, 13, 13, 13, 208 | 13, 13, 13, 13, 13, 13, 13, 13, 209 | 13, 13, 13, 13, 13, 13, 13, 13, 210 | 13, 13, 13, 13, 13, 13, 13, 13, 211 | 13, 13, 13, 13, 13, 13, 13, 13, 212 | 13, 13, 13, 13, 13, 13, 13, 13, 213 | 13, 13, 13, 13, 13, 13, 13, 13, 214 | 13, 13, 13, 13, 13, 13, 13, 13, 215 | 13, 13, 13, 13, 13, 13, 13, 13, 216 | 13, 13, 13, 13, 13, 13, 13, 13, 217 | 13, 13, 13, 7, 7, 7, 7, 7, 218 | 7, 7, 7, 0 219 | }; 220 | 221 | static const int policy_parser_start = 1; 222 | static const int policy_parser_first_final = 99; 223 | static const int policy_parser_error = 0; 224 | 225 | static const int policy_parser_en_main = 1; 226 | 227 | 228 | #line 95 "policy.rl" 229 | 230 | Policy *ParsePolicy(const char *p) { 231 | Policy *policy = malloc(sizeof(Policy)); 232 | 233 | memset(policy, 0, sizeof(Policy)); 234 | host.addr = NULL; 235 | 236 | #line 237 "policy.c" 237 | { 238 | policy->cs = policy_parser_start; 239 | } 240 | 241 | #line 102 "policy.rl" 242 | 243 | policy->p = p; 244 | policy->pe = p + strlen(p); 245 | policy->eof = policy->pe; 246 | 247 | 248 | #line 249 "policy.c" 249 | { 250 | int _klen; 251 | unsigned int _trans; 252 | const char *_acts; 253 | unsigned int _nacts; 254 | const char *_keys; 255 | 256 | if ( ( policy->p) == ( policy->pe) ) 257 | goto _test_eof; 258 | if ( policy->cs == 0 ) 259 | goto _out; 260 | _resume: 261 | _keys = _policy_parser_trans_keys + _policy_parser_key_offsets[ policy->cs]; 262 | _trans = _policy_parser_index_offsets[ policy->cs]; 263 | 264 | _klen = _policy_parser_single_lengths[ policy->cs]; 265 | if ( _klen > 0 ) { 266 | const char *_lower = _keys; 267 | const char *_mid; 268 | const char *_upper = _keys + _klen - 1; 269 | while (1) { 270 | if ( _upper < _lower ) 271 | break; 272 | 273 | _mid = _lower + ((_upper-_lower) >> 1); 274 | if ( (*( policy->p)) < *_mid ) 275 | _upper = _mid - 1; 276 | else if ( (*( policy->p)) > *_mid ) 277 | _lower = _mid + 1; 278 | else { 279 | _trans += (unsigned int)(_mid - _keys); 280 | goto _match; 281 | } 282 | } 283 | _keys += _klen; 284 | _trans += _klen; 285 | } 286 | 287 | _klen = _policy_parser_range_lengths[ policy->cs]; 288 | if ( _klen > 0 ) { 289 | const char *_lower = _keys; 290 | const char *_mid; 291 | const char *_upper = _keys + (_klen<<1) - 2; 292 | while (1) { 293 | if ( _upper < _lower ) 294 | break; 295 | 296 | _mid = _lower + (((_upper-_lower) >> 1) & ~1); 297 | if ( (*( policy->p)) < _mid[0] ) 298 | _upper = _mid - 2; 299 | else if ( (*( policy->p)) > _mid[1] ) 300 | _lower = _mid + 2; 301 | else { 302 | _trans += (unsigned int)((_mid - _keys)>>1); 303 | goto _match; 304 | } 305 | } 306 | _trans += _klen; 307 | } 308 | 309 | _match: 310 | _trans = _policy_parser_indicies[_trans]; 311 | policy->cs = _policy_parser_trans_targs[_trans]; 312 | 313 | if ( _policy_parser_trans_actions[_trans] == 0 ) 314 | goto _again; 315 | 316 | _acts = _policy_parser_actions + _policy_parser_trans_actions[_trans]; 317 | _nacts = (unsigned int) *_acts++; 318 | while ( _nacts-- > 0 ) 319 | { 320 | switch ( *_acts++ ) 321 | { 322 | case 0: 323 | #line 18 "policy.rl" 324 | { 325 | addr_p = 0; 326 | host.addr = NULL; 327 | have_addr = 0; 328 | } 329 | break; 330 | case 1: 331 | #line 24 "policy.rl" 332 | { 333 | have_addr = 1; 334 | } 335 | break; 336 | case 2: 337 | #line 28 "policy.rl" 338 | { 339 | host.port = 0; 340 | } 341 | break; 342 | case 3: 343 | #line 32 "policy.rl" 344 | { 345 | if (host.addr == NULL) { 346 | host.addr = malloc(16 * sizeof(char)); 347 | } 348 | host.addr[addr_p] = (*( policy->p)); 349 | addr_p++; 350 | } 351 | break; 352 | case 4: 353 | #line 40 "policy.rl" 354 | { 355 | host.port = host.port * 10 + ((*( policy->p)) - '0'); 356 | } 357 | break; 358 | case 5: 359 | #line 44 "policy.rl" 360 | { 361 | host.addr[addr_p] = '\0'; 362 | } 363 | break; 364 | case 6: 365 | #line 48 "policy.rl" 366 | { 367 | if (!have_addr) { 368 | free(host.addr); 369 | host.addr = NULL; 370 | } 371 | policy->listen = host; 372 | host.addr = NULL; 373 | } 374 | break; 375 | case 7: 376 | #line 57 "policy.rl" 377 | { 378 | if (!have_addr) { 379 | free(host.addr); 380 | host.addr = NULL; 381 | } 382 | policy->nhost++; 383 | policy->hosts = realloc(policy->hosts, sizeof(Hostent) * policy->nhost); 384 | policy->hosts[policy->nhost - 1] = host; 385 | host.addr = NULL; 386 | } 387 | break; 388 | case 8: 389 | #line 68 "policy.rl" 390 | { 391 | policy->type = PROXY_RR; 392 | } 393 | break; 394 | case 9: 395 | #line 72 "policy.rl" 396 | { 397 | policy->type = PROXY_HASH; 398 | } 399 | break; 400 | case 10: 401 | #line 76 "policy.rl" 402 | { 403 | LogFatal("policy syntax error around:\"%s\"\n", ( policy->p)); 404 | } 405 | break; 406 | #line 407 "policy.c" 407 | } 408 | } 409 | 410 | _again: 411 | if ( policy->cs == 0 ) 412 | goto _out; 413 | if ( ++( policy->p) != ( policy->pe) ) 414 | goto _resume; 415 | _test_eof: {} 416 | if ( ( policy->p) == ( policy->eof) ) 417 | { 418 | const char *__acts = _policy_parser_actions + _policy_parser_eof_actions[ policy->cs]; 419 | unsigned int __nacts = (unsigned int) *__acts++; 420 | while ( __nacts-- > 0 ) { 421 | switch ( *__acts++ ) { 422 | case 7: 423 | #line 57 "policy.rl" 424 | { 425 | if (!have_addr) { 426 | free(host.addr); 427 | host.addr = NULL; 428 | } 429 | policy->nhost++; 430 | policy->hosts = realloc(policy->hosts, sizeof(Hostent) * policy->nhost); 431 | policy->hosts[policy->nhost - 1] = host; 432 | host.addr = NULL; 433 | } 434 | break; 435 | case 10: 436 | #line 76 "policy.rl" 437 | { 438 | LogFatal("policy syntax error around:\"%s\"\n", ( policy->p)); 439 | } 440 | break; 441 | #line 442 "policy.c" 442 | } 443 | } 444 | } 445 | 446 | _out: {} 447 | } 448 | 449 | #line 108 "policy.rl" 450 | 451 | if (policy->cs == 452 | #line 453 "policy.c" 453 | 0 454 | #line 109 "policy.rl" 455 | ) { 456 | free(policy); 457 | return NULL; 458 | } 459 | 460 | return policy; 461 | } 462 | 463 | void FreePolicy(Policy *policy) { 464 | int i; 465 | free(policy->listen.addr); 466 | for (i = 0; i < policy->nhost; i++) { 467 | free(policy->hosts[i].addr); 468 | } 469 | free(policy->hosts); 470 | free(policy); 471 | } 472 | -------------------------------------------------------------------------------- /src/policy.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLICY_H_ 2 | #define _POLICY_H_ 3 | 4 | #include "util.h" 5 | 6 | #define PROXY_RR 0 7 | #define PROXY_HASH 1 8 | 9 | typedef struct Hostent { 10 | char *addr; 11 | int port; 12 | } Hostent; 13 | 14 | typedef struct Policy { 15 | Hostent listen; 16 | 17 | int type; 18 | 19 | Hostent *hosts; 20 | int nhost; 21 | 22 | int curhost; 23 | 24 | //ragel stuff 25 | const char *p, *pe, *eof; 26 | int cs; 27 | } Policy; 28 | 29 | void FreePolicy(Policy *policy); 30 | Policy *ParsePolicy(const char *str); 31 | 32 | #endif /* _POLICY_H_ */ 33 | 34 | -------------------------------------------------------------------------------- /src/policy.rl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "policy.h" 6 | 7 | static Hostent host; 8 | static int addr_p; 9 | static int have_addr; 10 | 11 | %%{ 12 | machine policy_parser; 13 | access policy->; 14 | variable p policy->p; 15 | variable pe policy->pe; 16 | variable eof policy->eof; 17 | 18 | action init_host { 19 | addr_p = 0; 20 | host.addr = NULL; 21 | have_addr = 0; 22 | } 23 | 24 | action have_addr { 25 | have_addr = 1; 26 | } 27 | 28 | action init_port { 29 | host.port = 0; 30 | } 31 | 32 | action append_addr { 33 | if (host.addr == NULL) { 34 | host.addr = malloc(16 * sizeof(char)); 35 | } 36 | host.addr[addr_p] = fc; 37 | addr_p++; 38 | } 39 | 40 | action append_port { 41 | host.port = host.port * 10 + (fc - '0'); 42 | } 43 | 44 | action finish_addr { 45 | host.addr[addr_p] = '\0'; 46 | } 47 | 48 | action listen_addr { 49 | if (!have_addr) { 50 | free(host.addr); 51 | host.addr = NULL; 52 | } 53 | policy->listen = host; 54 | host.addr = NULL; 55 | } 56 | 57 | action append_host { 58 | if (!have_addr) { 59 | free(host.addr); 60 | host.addr = NULL; 61 | } 62 | policy->nhost++; 63 | policy->hosts = realloc(policy->hosts, sizeof(Hostent) * policy->nhost); 64 | policy->hosts[policy->nhost - 1] = host; 65 | host.addr = NULL; 66 | } 67 | 68 | action set_rr { 69 | policy->type = PROXY_RR; 70 | } 71 | 72 | action set_hash { 73 | policy->type = PROXY_HASH; 74 | } 75 | 76 | action error { 77 | LogFatal("policy syntax error around:\"%s\"\n", fpc); 78 | } 79 | 80 | ws = (' '); 81 | port = (digit {1,5}); 82 | dottedip = (digit {1,3} '.' digit {1,3} '.' digit {1,3} '.' digit {1,3}); 83 | addr = ('localhost' | 'any' | dottedip) $append_addr %finish_addr; 84 | host = ((addr ':' >have_addr)? port >init_port $append_port) >init_host; 85 | 86 | type = ('rr' %set_rr | 'hash' %set_hash); 87 | group = (type ws* '{' ws* host (ws+ >append_host host)* ws* '}' >append_host); 88 | 89 | policy = (host %listen_addr ws* '->' ws* (host >set_rr %append_host | group)); 90 | 91 | main := (policy) $!error; 92 | }%% 93 | 94 | %% write data; 95 | 96 | Policy *ParsePolicy(const char *p) { 97 | Policy *policy = malloc(sizeof(Policy)); 98 | 99 | memset(policy, 0, sizeof(Policy)); 100 | host.addr = NULL; 101 | %% write init; 102 | 103 | policy->p = p; 104 | policy->pe = p + strlen(p); 105 | policy->eof = policy->pe; 106 | 107 | %% write exec; 108 | 109 | if (policy->cs == %%{write error;}%%) { 110 | free(policy); 111 | return NULL; 112 | } 113 | 114 | return policy; 115 | } 116 | 117 | void FreePolicy(Policy *policy) { 118 | int i; 119 | free(policy->listen.addr); 120 | for (i = 0; i < policy->nhost; i++) { 121 | free(policy->hosts[i].addr); 122 | } 123 | free(policy->hosts); 124 | free(policy); 125 | } 126 | -------------------------------------------------------------------------------- /src/tcproxy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "policy.h" 12 | #include "util.h" 13 | #include "ae.h" 14 | #include "anet.h" 15 | 16 | #define MAX_WRITE_PER_EVENT 1024*1024*1024 17 | #define CLIENT_CLOSE_AFTER_SENT 0x01 18 | #define VERSION "0.9.2" 19 | 20 | Policy *policy; 21 | static int run_daemonize = 0; 22 | static char error_[1024]; 23 | aeEventLoop *el; 24 | 25 | typedef struct Client { 26 | int fd; 27 | int flags; 28 | 29 | struct Client *remote; 30 | BufferList *blist; 31 | 32 | void (*OnError)(struct Client *c); 33 | void (*OnRemoteDown)(struct Client *c); 34 | } Client; 35 | 36 | void FreeRemote(Client *c); 37 | void ReadIncome(aeEventLoop *el, int fd, void *privdata, int mask); 38 | 39 | void Usage() { 40 | printf("usage:\n" 41 | " tcproxy [options] \"proxy policy\"\n" 42 | "options:\n" 43 | " -l file specify log file\n" 44 | " -d run in background\n" 45 | " -v show detailed log\n" 46 | " --version show version and exit\n" 47 | " -h show help and exit\n\n" 48 | "examples:\n" 49 | " tcproxy \"11212 -> 11211\"\n" 50 | " tcproxy \"127.0.0.1:6379 -> rr{192.168.0.100:6379 192.168.0.101:6379}\"\n\n" 51 | ); 52 | exit(EXIT_SUCCESS); 53 | } 54 | 55 | void ParseArgs(int argc, char **argv) { 56 | int i, j; 57 | const char *logfile = "stderr"; 58 | int loglevel = kError; 59 | 60 | InitLogger(loglevel, NULL); 61 | 62 | for (i = 1; i < argc; i++) { 63 | if (argv[i][0] == '-') { 64 | if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 65 | Usage(); 66 | } else if (!strcmp(argv[i], "--version")) { 67 | printf("tcproxy "VERSION"\n\n"); 68 | exit(EXIT_SUCCESS); 69 | } else if (!strcmp(argv[i], "-d")) { 70 | run_daemonize = 1; 71 | } else if (!strcmp(argv[i], "-l")) { 72 | if (++i >= argc) LogFatal("file name must be specified"); 73 | logfile = argv[i]; 74 | } else if (!strncmp(argv[i], "-v", 2)) { 75 | for (j = 1; argv[i][j] != '\0'; j++) { 76 | if (argv[i][j] == 'v') loglevel++; 77 | else LogFatal("invalid argument %s", argv[i]);; 78 | } 79 | } else { 80 | LogFatal("unknow option %s\n", argv[i]); 81 | } 82 | } else { 83 | policy = ParsePolicy(argv[i]); 84 | } 85 | } 86 | 87 | InitLogger(loglevel, logfile); 88 | 89 | if (policy == NULL) { 90 | LogFatal("policy not valid"); 91 | } 92 | } 93 | 94 | void SignalHandler(int signo) { 95 | if (signo == SIGINT || signo == SIGTERM) { 96 | el->stop = 1; 97 | } 98 | } 99 | 100 | void RemoteDown(Client *r) { 101 | r->remote->OnRemoteDown(r->remote); 102 | } 103 | 104 | Client *AllocRemote(Client *c) { 105 | Client *r = malloc(sizeof(Client)); 106 | r->flags = 0; 107 | int fd = anetTcpNonBlockConnect(error_, policy->hosts[0].addr, policy->hosts[0].port); 108 | 109 | if (r == NULL || fd == -1) return NULL; 110 | LogDebug("connect remote fd %d", fd); 111 | anetNonBlock(NULL, fd); 112 | anetTcpNoDelay(NULL, fd); 113 | r->fd = fd; 114 | r->remote = c; 115 | r->OnError = RemoteDown; 116 | r->blist = AllocBufferList(3); 117 | if (aeCreateFileEvent(el, r->fd, AE_READABLE, ReadIncome, r) == AE_ERR) { 118 | close(fd); 119 | return NULL; 120 | } 121 | 122 | LogDebug("new remote %d %d", r->fd, c->fd); 123 | 124 | return r; 125 | } 126 | 127 | void FreeClient(Client *c) { 128 | if (c == NULL) return; 129 | LogDebug("free client %d", c->fd); 130 | aeDeleteFileEvent(el, c->fd, AE_READABLE); 131 | aeDeleteFileEvent(el, c->fd, AE_WRITABLE); 132 | close(c->fd); 133 | FreeRemote(c->remote); 134 | FreeBufferList(c->blist); 135 | free(c); 136 | } 137 | 138 | void CloseAfterSent(Client *c) { 139 | int len; 140 | if (BufferListGetData(c->blist, &len) == NULL) { 141 | // no data remains to be sent, close this client 142 | FreeClient(c); 143 | } else { 144 | c->flags |= CLIENT_CLOSE_AFTER_SENT; 145 | } 146 | } 147 | 148 | void ReAllocRemote(Client *c) { 149 | // TODO 150 | } 151 | 152 | Client *AllocClient(int fd) { 153 | Client *c = malloc(sizeof(Client)); 154 | c->flags = 0; 155 | if (c == NULL) return NULL; 156 | 157 | anetNonBlock(NULL, fd); 158 | anetTcpNoDelay(NULL, fd); 159 | 160 | c->fd = fd; 161 | c->blist = AllocBufferList(3); 162 | c->remote = AllocRemote(c); 163 | c->OnError = FreeClient; 164 | // c->OnRemoteDown = ReAllocRemote; 165 | c->OnRemoteDown = CloseAfterSent; // freeclient temprarily before hot switch done 166 | if (c->remote == NULL) { 167 | close(fd); 168 | free(c); 169 | return NULL; 170 | } 171 | 172 | LogDebug("New client fd:%d remotefd:%d", c->fd, c->remote->fd); 173 | 174 | return c; 175 | } 176 | 177 | void FreeRemote(Client *r) { 178 | LogDebug("free remote"); 179 | aeDeleteFileEvent(el, r->fd, AE_READABLE); 180 | aeDeleteFileEvent(el, r->fd, AE_WRITABLE); 181 | close(r->fd); 182 | FreeBufferList(r->blist); 183 | free(r); 184 | } 185 | 186 | void SendOutcome(aeEventLoop *el, int fd, void *privdata, int mask) { 187 | LogDebug("SendOutcome"); 188 | Client *c = (Client*)privdata; 189 | int len, nwritten = 0, totwritten = 0; 190 | char *buf; 191 | 192 | buf = BufferListGetData(c->blist, &len); 193 | if (buf == NULL) { 194 | LogDebug("delete write event"); 195 | aeDeleteFileEvent(el, fd, AE_WRITABLE); 196 | } 197 | 198 | while (1) { 199 | buf = BufferListGetData(c->blist, &len); 200 | if (buf == NULL) { 201 | // no data to send 202 | if (c->flags & CLIENT_CLOSE_AFTER_SENT) { 203 | FreeClient(c); 204 | return; 205 | } 206 | break; 207 | } 208 | nwritten = send(fd, buf, len, MSG_DONTWAIT); 209 | if (nwritten <= 0) break; 210 | 211 | totwritten += nwritten; 212 | LogDebug("write and pop data %p %d", c->blist, nwritten); 213 | BufferListPop(c->blist, nwritten); 214 | /* Note that we avoid to send more than MAX_WRITE_PER_EVENT 215 | * bytes, in a single threaded server it's a good idea to serve 216 | * other clients as well, even if a very large request comes from 217 | * super fast link that is always able to accept data*/ 218 | if (totwritten > MAX_WRITE_PER_EVENT) break; 219 | } 220 | 221 | LogDebug("totwritten %d", totwritten); 222 | 223 | if (nwritten == -1) { 224 | if (errno == EAGAIN) { 225 | nwritten = 0; 226 | } else { 227 | LogDebug("write error %s", strerror(errno)); 228 | c->OnError(c); 229 | return; 230 | } 231 | } 232 | } 233 | 234 | int SetWriteEvent(Client *c) { 235 | if (aeCreateFileEvent(el, c->fd, AE_WRITABLE, SendOutcome, c) == AE_ERR) { 236 | LogError("Set write event failed"); 237 | return -1; 238 | } 239 | return 0; 240 | } 241 | 242 | void ReadIncome(aeEventLoop *el, int fd, void *privdata, int mask) { 243 | LogDebug("read in come"); 244 | Client *c = (Client*)privdata; 245 | Client *r = c->remote; 246 | char *buf; 247 | int len, nread = 0; 248 | 249 | while (1) { 250 | buf = BufferListGetSpace(r->blist, &len); 251 | if (buf == NULL) break; 252 | nread = recv(fd, buf, len, 0); 253 | if (nread == -1) { 254 | if (errno == EAGAIN) { 255 | // no data 256 | nread = 0; 257 | } else { 258 | // connection error 259 | goto ERROR; 260 | } 261 | } else if (nread == 0) { 262 | // connection closed 263 | LogInfo("connection closed"); 264 | goto ERROR; 265 | } 266 | 267 | if (nread) { 268 | BufferListPush(r->blist, nread); 269 | SetWriteEvent(r); 270 | LogDebug("set write"); 271 | } else { 272 | break; 273 | } 274 | } 275 | 276 | return; 277 | 278 | ERROR: 279 | c->OnError(c); 280 | } 281 | 282 | void AcceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { 283 | int cport, cfd; 284 | char cip[128]; 285 | 286 | cfd = anetTcpAccept(error_, fd, cip, &cport); 287 | if (cfd == AE_ERR) { 288 | LogError("Accept client connection failed: %s", error_); 289 | return; 290 | } 291 | LogInfo("Accepted client from %s:%d", cip, cport); 292 | 293 | Client *c = AllocClient(cfd); 294 | 295 | if (c == NULL || aeCreateFileEvent(el, cfd, AE_READABLE, ReadIncome, c) == AE_ERR) { 296 | LogError("Create event failed"); 297 | FreeClient(c); 298 | } 299 | } 300 | 301 | int main(int argc, char **argv) { 302 | int i, listen_fd; 303 | struct sigaction sig_action; 304 | 305 | ParseArgs(argc, argv); 306 | 307 | if (run_daemonize) Daemonize(); 308 | 309 | sig_action.sa_handler = SignalHandler; 310 | sig_action.sa_flags = SA_RESTART; 311 | sigemptyset(&sig_action.sa_mask); 312 | sigaction(SIGINT, &sig_action, NULL); 313 | sigaction(SIGTERM, &sig_action, NULL); 314 | sigaction(SIGPIPE, &sig_action, NULL); 315 | 316 | if ((policy->listen.addr == NULL) || !strcmp(policy->listen.addr, "any")) { 317 | free(policy->listen.addr); 318 | policy->listen.addr = strdup("0.0.0.0"); 319 | } else if (!strcmp(policy->listen.addr, "localhost")) { 320 | free(policy->listen.addr); 321 | policy->listen.addr = strdup("127.0.0.1"); 322 | } 323 | 324 | listen_fd = anetTcpServer(error_, policy->listen.port, policy->listen.addr); 325 | 326 | el = aeCreateEventLoop(65536); 327 | 328 | if (listen_fd < 0 || aeCreateFileEvent(el, listen_fd, AE_READABLE, AcceptTcpHandler, NULL) == AE_ERR) { 329 | LogFatal("listen failed: %s", strerror(errno)); 330 | } 331 | 332 | LogInfo("listenning on %s:%d", (policy->listen.addr? policy->listen.addr : "any"), policy->listen.port); 333 | for (i = 0; i < policy->nhost; i++) { 334 | if (policy->hosts[i].addr == NULL) policy->hosts[i].addr = strdup("127.0.0.1"); 335 | LogInfo("proxy to %s:%d", policy->hosts[i].addr, policy->hosts[i].port); 336 | } 337 | 338 | aeMain(el); 339 | 340 | aeDeleteEventLoop(el); 341 | 342 | FreePolicy(policy); 343 | 344 | return 0; 345 | } 346 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util.h" 12 | 13 | static LogLevel log_level = kDebug; 14 | static FILE *log_file = NULL; 15 | static char now_str[sizeof("2011/11/11 11:11:11")]; 16 | static const char *LevelName[] = { 17 | "NONE", 18 | "FATAL", 19 | "CRITICAL", 20 | "ERROR", 21 | "WARNING", 22 | "INFO", 23 | "DEBUG", 24 | }; 25 | 26 | static void UpdateTime() { 27 | static time_t now = 0; 28 | time_t t = time(NULL); 29 | 30 | //update time every second 31 | if (t - now == 0) return; 32 | now = t; 33 | 34 | struct tm tm; 35 | localtime_r(&now, &tm); 36 | sprintf(now_str, "%04d/%02d/%02d %02d:%02d:%02d", 37 | 1900 + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, 38 | tm.tm_hour, tm.tm_min, tm.tm_sec); 39 | } 40 | 41 | void LogPrint(LogLevel level, const char *fmt, ...) { 42 | va_list args; 43 | if (level > log_level) return; 44 | va_start(args, fmt); 45 | if (log_file) vfprintf(log_file, fmt, args); 46 | va_end(args); 47 | fflush(log_file); 48 | } 49 | 50 | void LogInternal(LogLevel level, const char *fmt, ...) { 51 | va_list args; 52 | if (level > log_level) return; 53 | UpdateTime(); 54 | if (log_file) fprintf(log_file, "%s [%s] ", now_str, LevelName[level]); 55 | va_start(args, fmt); 56 | if (log_file) vfprintf(log_file, fmt, args); 57 | va_end(args); 58 | fflush(log_file); 59 | } 60 | 61 | void InitLogger(LogLevel level, const char *filename) { 62 | log_level = level; 63 | 64 | if (filename == NULL || strcmp(filename, "stderr") == 0 || strcmp(filename, "") == 0) { 65 | log_file = stderr; 66 | } else if (strcmp(filename, "stdout") == 0) { 67 | log_file = stdout; 68 | } else { 69 | log_file = fopen(filename, "a+"); 70 | } 71 | } 72 | 73 | 74 | void Daemonize() { 75 | int fd; 76 | 77 | if (fork() != 0) exit(0); /* parent exits */ 78 | 79 | setsid(); /* create a new session */ 80 | 81 | if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { 82 | dup2(fd, STDIN_FILENO); 83 | dup2(fd, STDOUT_FILENO); 84 | dup2(fd, STDERR_FILENO); 85 | if (fd > STDERR_FILENO) close(fd); 86 | } 87 | } 88 | 89 | BufferList *AllocBufferList(int n) { 90 | BufferList *blist = malloc(sizeof(BufferList)); 91 | BufferListNode *buf = malloc(sizeof(BufferListNode)); 92 | BufferListNode *pre; 93 | int i; 94 | 95 | buf->size = 0; 96 | buf->next = NULL; 97 | 98 | blist->head = buf; 99 | pre = blist->head = blist->write_node = buf; 100 | 101 | for (i = 1; i < n; i++) { 102 | buf = malloc(sizeof(BufferListNode)); 103 | buf->size = 0; 104 | buf->next = NULL; 105 | pre->next = buf; 106 | pre = buf; 107 | } 108 | 109 | blist->tail = buf; 110 | 111 | blist->read_pos = 0; 112 | 113 | return blist; 114 | } 115 | 116 | void FreeBufferList(BufferList *blist) { 117 | BufferListNode *cur = blist->head; 118 | while (cur != NULL) { 119 | blist->head = cur->next; 120 | free(cur); 121 | cur = blist->head; 122 | } 123 | free(blist); 124 | } 125 | 126 | // get free space from current write node 127 | char *BufferListGetSpace(BufferList *blist, int *len) { 128 | if (blist->write_node == blist->tail && blist->write_node->size == BUFFER_CHUNK_SIZE) { 129 | *len = 0; 130 | LogDebug("tail full"); 131 | return NULL; 132 | } 133 | *len = BUFFER_CHUNK_SIZE - blist->write_node->size; 134 | return blist->write_node->data + blist->write_node->size; 135 | } 136 | 137 | // push data into buffer 138 | void BufferListPush(BufferList *blist, int len) { 139 | blist->write_node->size += len; 140 | LogDebug("head %p tail %p cur %p data %d", blist->head, blist->tail, blist->write_node, blist->head->size - blist->read_pos); 141 | if (blist->write_node->size == BUFFER_CHUNK_SIZE && blist->write_node != blist->tail) { 142 | // move to next chunk 143 | blist->write_node = blist->write_node->next; 144 | } 145 | } 146 | 147 | // always get data from head 148 | char *BufferListGetData(BufferList *blist, int *len) { 149 | if (blist->head == blist->write_node && blist->read_pos == blist->head->size) { 150 | *len = 0; 151 | LogDebug("head empty"); 152 | return NULL; 153 | } 154 | *len = blist->head->size - blist->read_pos; 155 | return blist->head->data + blist->read_pos; 156 | } 157 | 158 | // pop data out from buffer 159 | void BufferListPop(BufferList *blist, int len) { 160 | blist->read_pos += len; 161 | LogDebug("head %p tail %p cur %p data %d", blist->head, blist->tail, blist->write_node, blist->head->size - blist->read_pos); 162 | if (blist->read_pos == blist->head->size && blist->head != blist->write_node) { 163 | // head empty, and head is not the node we are writing into, move to tail 164 | BufferListNode *cur = blist->head; 165 | blist->head = blist->head->next; 166 | blist->tail->next = cur; 167 | blist->tail = cur; 168 | cur->size = 0; 169 | cur->next = NULL; 170 | blist->read_pos = 0; 171 | if (blist->head == NULL) { 172 | // there is only one chunk in buffer list 173 | LogDebug("head null"); 174 | exit(0); 175 | blist->head = blist->tail; 176 | } 177 | } 178 | // else leave it there, further get data will return NULL 179 | } 180 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H_ 2 | #define _UTIL_H_ 3 | 4 | #define BUFFER_CHUNK_SIZE 1024*1024*2 5 | 6 | typedef enum LogLevel { 7 | kNone = 0, 8 | kFatal, 9 | kCritical, 10 | kError, 11 | kWarning, 12 | kInfo, 13 | kDebug, 14 | } LogLevel; 15 | 16 | #define LogInfo(s...) do {\ 17 | LogInternal(kInfo, s);\ 18 | LogPrint(kInfo, "\n"); \ 19 | }while(0) 20 | 21 | #define LogWarning(s...) do {\ 22 | LogInternal(kWarning, s);\ 23 | LogPrint(kWarning, "\n"); \ 24 | }while(0) 25 | 26 | #define LogError(s...) do {\ 27 | LogInternal(kError, s);\ 28 | LogPrint(kError, "\n"); \ 29 | }while(0) 30 | 31 | #define LogCritical(s...) do {\ 32 | LogInternal(kCritical, s);\ 33 | LogPrint(kCritical, "\n"); \ 34 | }while(0) 35 | 36 | #define LogFatal(s...) do {\ 37 | LogInternal(kFatal, s);\ 38 | LogPrint(kFatal, "\n"); \ 39 | exit(EXIT_FAILURE);\ 40 | }while(0) 41 | 42 | #ifdef DEBUG 43 | #define LogDebug(s...) do {\ 44 | LogInternal(kDebug, s);\ 45 | LogPrint(kDebug, " [%s]", __PRETTY_FUNCTION__);\ 46 | LogPrint(kDebug, "\n"); \ 47 | }while(0) 48 | #else 49 | #define LogDebug(s...) 50 | #endif 51 | 52 | void InitLogger(LogLevel level, const char *filename); 53 | void LogInternal(LogLevel level, const char *fmt, ...); 54 | void LogPrint(LogLevel level, const char *fmt, ...); 55 | 56 | typedef struct BufferListNode { 57 | char data[BUFFER_CHUNK_SIZE]; 58 | int size; 59 | struct BufferListNode *next; 60 | } BufferListNode; 61 | 62 | typedef struct BufferList { 63 | BufferListNode *head; 64 | BufferListNode *tail; 65 | int read_pos; 66 | BufferListNode *write_node; 67 | } BufferList; 68 | 69 | BufferList *AllocBufferList(int n); 70 | 71 | void FreeBufferList(BufferList *blist); 72 | char *BufferListGetData(BufferList *blist, int *len); 73 | char *BufferListGetSpace(BufferList *blist, int *len); 74 | void BufferListPop(BufferList *blist, int len); 75 | void BufferListPush(BufferList *blist, int len); 76 | 77 | void Daemonize(); 78 | 79 | #endif /* _UTIL_H_ */ 80 | -------------------------------------------------------------------------------- /src/zmalloc.c: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | /* This function provide us access to the original libc free(). This is useful 35 | * for instance to free results obtained by backtrace_symbols(). We need 36 | * to define this function before including zmalloc.h that may shadow the 37 | * free implementation if we use jemalloc or another non standard allocator. */ 38 | void zlibc_free(void *ptr) { 39 | free(ptr); 40 | } 41 | 42 | #include 43 | #include 44 | #include "config.h" 45 | #include "zmalloc.h" 46 | 47 | #ifdef HAVE_MALLOC_SIZE 48 | #define PREFIX_SIZE (0) 49 | #else 50 | #if defined(__sun) || defined(__sparc) || defined(__sparc__) 51 | #define PREFIX_SIZE (sizeof(long long)) 52 | #else 53 | #define PREFIX_SIZE (sizeof(size_t)) 54 | #endif 55 | #endif 56 | 57 | /* Explicitly override malloc/free etc when using tcmalloc. */ 58 | #if defined(USE_TCMALLOC) 59 | #define malloc(size) tc_malloc(size) 60 | #define calloc(count,size) tc_calloc(count,size) 61 | #define realloc(ptr,size) tc_realloc(ptr,size) 62 | #define free(ptr) tc_free(ptr) 63 | #elif defined(USE_JEMALLOC) 64 | #define malloc(size) je_malloc(size) 65 | #define calloc(count,size) je_calloc(count,size) 66 | #define realloc(ptr,size) je_realloc(ptr,size) 67 | #define free(ptr) je_free(ptr) 68 | #endif 69 | 70 | #ifdef HAVE_ATOMIC 71 | #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n)) 72 | #define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n)) 73 | #else 74 | #define update_zmalloc_stat_add(__n) do { \ 75 | pthread_mutex_lock(&used_memory_mutex); \ 76 | used_memory += (__n); \ 77 | pthread_mutex_unlock(&used_memory_mutex); \ 78 | } while(0) 79 | 80 | #define update_zmalloc_stat_sub(__n) do { \ 81 | pthread_mutex_lock(&used_memory_mutex); \ 82 | used_memory -= (__n); \ 83 | pthread_mutex_unlock(&used_memory_mutex); \ 84 | } while(0) 85 | 86 | #endif 87 | 88 | #define update_zmalloc_stat_alloc(__n,__size) do { \ 89 | size_t _n = (__n); \ 90 | if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 91 | if (zmalloc_thread_safe) { \ 92 | update_zmalloc_stat_add(_n); \ 93 | } else { \ 94 | used_memory += _n; \ 95 | } \ 96 | } while(0) 97 | 98 | #define update_zmalloc_stat_free(__n) do { \ 99 | size_t _n = (__n); \ 100 | if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 101 | if (zmalloc_thread_safe) { \ 102 | update_zmalloc_stat_sub(_n); \ 103 | } else { \ 104 | used_memory -= _n; \ 105 | } \ 106 | } while(0) 107 | 108 | static size_t used_memory = 0; 109 | static int zmalloc_thread_safe = 0; 110 | pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; 111 | 112 | static void zmalloc_oom(size_t size) { 113 | fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", 114 | size); 115 | fflush(stderr); 116 | abort(); 117 | } 118 | 119 | void *zmalloc(size_t size) { 120 | void *ptr = malloc(size+PREFIX_SIZE); 121 | 122 | if (!ptr) zmalloc_oom(size); 123 | #ifdef HAVE_MALLOC_SIZE 124 | update_zmalloc_stat_alloc(zmalloc_size(ptr),size); 125 | return ptr; 126 | #else 127 | *((size_t*)ptr) = size; 128 | update_zmalloc_stat_alloc(size+PREFIX_SIZE,size); 129 | return (char*)ptr+PREFIX_SIZE; 130 | #endif 131 | } 132 | 133 | void *zcalloc(size_t size) { 134 | void *ptr = calloc(1, size+PREFIX_SIZE); 135 | 136 | if (!ptr) zmalloc_oom(size); 137 | #ifdef HAVE_MALLOC_SIZE 138 | update_zmalloc_stat_alloc(zmalloc_size(ptr),size); 139 | return ptr; 140 | #else 141 | *((size_t*)ptr) = size; 142 | update_zmalloc_stat_alloc(size+PREFIX_SIZE,size); 143 | return (char*)ptr+PREFIX_SIZE; 144 | #endif 145 | } 146 | 147 | void *zrealloc(void *ptr, size_t size) { 148 | #ifndef HAVE_MALLOC_SIZE 149 | void *realptr; 150 | #endif 151 | size_t oldsize; 152 | void *newptr; 153 | 154 | if (ptr == NULL) return zmalloc(size); 155 | #ifdef HAVE_MALLOC_SIZE 156 | oldsize = zmalloc_size(ptr); 157 | newptr = realloc(ptr,size); 158 | if (!newptr) zmalloc_oom(size); 159 | 160 | update_zmalloc_stat_free(oldsize); 161 | update_zmalloc_stat_alloc(zmalloc_size(newptr),size); 162 | return newptr; 163 | #else 164 | realptr = (char*)ptr-PREFIX_SIZE; 165 | oldsize = *((size_t*)realptr); 166 | newptr = realloc(realptr,size+PREFIX_SIZE); 167 | if (!newptr) zmalloc_oom(size); 168 | 169 | *((size_t*)newptr) = size; 170 | update_zmalloc_stat_free(oldsize); 171 | update_zmalloc_stat_alloc(size,size); 172 | return (char*)newptr+PREFIX_SIZE; 173 | #endif 174 | } 175 | 176 | /* Provide zmalloc_size() for systems where this function is not provided by 177 | * malloc itself, given that in that case we store an header with this 178 | * information as the first bytes of every allocation. */ 179 | #ifndef HAVE_MALLOC_SIZE 180 | size_t zmalloc_size(void *ptr) { 181 | void *realptr = (char*)ptr-PREFIX_SIZE; 182 | size_t size = *((size_t*)realptr); 183 | /* Assume at least that all the allocations are padded at sizeof(long) by 184 | * the underlying allocator. */ 185 | if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1)); 186 | return size+PREFIX_SIZE; 187 | } 188 | #endif 189 | 190 | void zfree(void *ptr) { 191 | #ifndef HAVE_MALLOC_SIZE 192 | void *realptr; 193 | size_t oldsize; 194 | #endif 195 | 196 | if (ptr == NULL) return; 197 | #ifdef HAVE_MALLOC_SIZE 198 | update_zmalloc_stat_free(zmalloc_size(ptr)); 199 | free(ptr); 200 | #else 201 | realptr = (char*)ptr-PREFIX_SIZE; 202 | oldsize = *((size_t*)realptr); 203 | update_zmalloc_stat_free(oldsize+PREFIX_SIZE); 204 | free(realptr); 205 | #endif 206 | } 207 | 208 | char *zstrdup(const char *s) { 209 | size_t l = strlen(s)+1; 210 | char *p = zmalloc(l); 211 | 212 | memcpy(p,s,l); 213 | return p; 214 | } 215 | 216 | size_t zmalloc_used_memory(void) { 217 | size_t um; 218 | 219 | if (zmalloc_thread_safe) { 220 | #ifdef HAVE_ATOMIC 221 | um = __sync_add_and_fetch(&used_memory, 0); 222 | #else 223 | pthread_mutex_lock(&used_memory_mutex); 224 | um = used_memory; 225 | pthread_mutex_unlock(&used_memory_mutex); 226 | #endif 227 | } 228 | else { 229 | um = used_memory; 230 | } 231 | 232 | return um; 233 | } 234 | 235 | void zmalloc_enable_thread_safeness(void) { 236 | zmalloc_thread_safe = 1; 237 | } 238 | 239 | /* Get the RSS information in an OS-specific way. 240 | * 241 | * WARNING: the function zmalloc_get_rss() is not designed to be fast 242 | * and may not be called in the busy loops where Redis tries to release 243 | * memory expiring or swapping out objects. 244 | * 245 | * For this kind of "fast RSS reporting" usages use instead the 246 | * function RedisEstimateRSS() that is a much faster (and less precise) 247 | * version of the funciton. */ 248 | 249 | #if defined(HAVE_PROCFS) 250 | #include 251 | #include 252 | #include 253 | #include 254 | 255 | size_t zmalloc_get_rss(void) { 256 | int page = sysconf(_SC_PAGESIZE); 257 | size_t rss; 258 | char buf[4096]; 259 | char filename[256]; 260 | int fd, count; 261 | char *p, *x; 262 | 263 | snprintf(filename,256,"/proc/%d/stat",getpid()); 264 | if ((fd = open(filename,O_RDONLY)) == -1) return 0; 265 | if (read(fd,buf,4096) <= 0) { 266 | close(fd); 267 | return 0; 268 | } 269 | close(fd); 270 | 271 | p = buf; 272 | count = 23; /* RSS is the 24th field in /proc//stat */ 273 | while(p && count--) { 274 | p = strchr(p,' '); 275 | if (p) p++; 276 | } 277 | if (!p) return 0; 278 | x = strchr(p,' '); 279 | if (!x) return 0; 280 | *x = '\0'; 281 | 282 | rss = strtoll(p,NULL,10); 283 | rss *= page; 284 | return rss; 285 | } 286 | #elif defined(HAVE_TASKINFO) 287 | #include 288 | #include 289 | #include 290 | #include 291 | #include 292 | #include 293 | #include 294 | 295 | size_t zmalloc_get_rss(void) { 296 | task_t task = MACH_PORT_NULL; 297 | struct task_basic_info t_info; 298 | mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; 299 | 300 | if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) 301 | return 0; 302 | task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); 303 | 304 | return t_info.resident_size; 305 | } 306 | #else 307 | size_t zmalloc_get_rss(void) { 308 | /* If we can't get the RSS in an OS-specific way for this system just 309 | * return the memory usage we estimated in zmalloc().. 310 | * 311 | * Fragmentation will appear to be always 1 (no fragmentation) 312 | * of course... */ 313 | return zmalloc_used_memory(); 314 | } 315 | #endif 316 | 317 | /* Fragmentation = RSS / allocated-bytes */ 318 | float zmalloc_get_fragmentation_ratio(void) { 319 | return (float)zmalloc_get_rss()/zmalloc_used_memory(); 320 | } 321 | -------------------------------------------------------------------------------- /src/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #define JEMALLOC_MANGLE 51 | #include 52 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 53 | #define HAVE_MALLOC_SIZE 1 54 | #define zmalloc_size(p) JEMALLOC_P(malloc_usable_size)(p) 55 | #else 56 | #error "Newer version of jemalloc required" 57 | #endif 58 | 59 | #elif defined(__APPLE__) 60 | #include 61 | #define HAVE_MALLOC_SIZE 1 62 | #define zmalloc_size(p) malloc_size(p) 63 | #endif 64 | 65 | #ifndef ZMALLOC_LIB 66 | #define ZMALLOC_LIB "libc" 67 | #endif 68 | 69 | void *zmalloc(size_t size); 70 | void *zcalloc(size_t size); 71 | void *zrealloc(void *ptr, size_t size); 72 | void zfree(void *ptr); 73 | char *zstrdup(const char *s); 74 | size_t zmalloc_used_memory(void); 75 | void zmalloc_enable_thread_safeness(void); 76 | float zmalloc_get_fragmentation_ratio(void); 77 | size_t zmalloc_get_rss(void); 78 | void zlibc_free(void *ptr); 79 | 80 | #ifndef HAVE_MALLOC_SIZE 81 | size_t zmalloc_size(void *ptr); 82 | #endif 83 | 84 | #endif /* __ZMALLOC_H */ 85 | --------------------------------------------------------------------------------