├── src ├── version.h ├── crc64.h ├── Makefile.am ├── sha1.h ├── mkreleasehdr.sh ├── config.h ├── udp_client.h ├── fmacros.h ├── util.h ├── tcp_client.h ├── solarisfixes.h ├── narcassert.h ├── endianconv.h ├── stream.h ├── adlist.h ├── sds.h ├── endianconv.c ├── crc16.c ├── debug.c ├── udp_client.c ├── setproctitle.c ├── narc.h ├── tcp_client.c ├── sha1.c ├── config.c ├── crc64.c ├── adlist.c ├── narc.c ├── stream.c ├── util.c └── sds.c ├── Makefile.am ├── .gitmodules ├── .gitignore ├── .travis.yml ├── README.md ├── configure.ac ├── Vagrantfile ├── narc.conf └── LICENSE /src/version.h: -------------------------------------------------------------------------------- 1 | #define NARC_VERSION "0.2.2" 2 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # -*- mode: Makefile; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | # vim: ts=8 sw=8 ft=Makefile noet 3 | 4 | SUBDIRS = src 5 | -------------------------------------------------------------------------------- /src/crc64.h: -------------------------------------------------------------------------------- 1 | #ifndef CRC64_H 2 | #define CRC64_H 3 | 4 | #include 5 | 6 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/libuv"] 2 | path = deps/libuv 3 | url = git@github.com:joyent/libuv.git 4 | [submodule "deps/jemalloc"] 5 | path = deps/jemalloc 6 | url = git@github.com:jemalloc/jemalloc.git 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | *.o 4 | *.a 5 | /config.* 6 | /src/hello 7 | /autom4te.cache 8 | /aclocal.m4 9 | /compile 10 | /configure 11 | /depcomp 12 | /install-sh 13 | /missing 14 | .deps 15 | .vagrant 16 | src/narcd -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | sudo: required 4 | dist: trusty 5 | 6 | before_install: 7 | - sudo add-apt-repository ppa:wesmason/nodejs-backport -y 8 | - sudo apt-get -qq update 9 | - sudo apt-get install -y libuv1 libuv1-dev 10 | - mkdir m4 11 | - autoreconf -fvi 12 | 13 | script: ./configure && make -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # -*- mode: Makefile; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | # vim: ts=8 sw=8 ft=Makefile noet 3 | 4 | bin_PROGRAMS = narcd 5 | 6 | narcd_SOURCES = adlist.c crc16.c endianconv.c narc.h sds.c sha1.h tcp_client.c util.c \ 7 | adlist.h crc64.c endianconv.h narcassert.h sds.h solarisfixes.h tcp_client.h util.h \ 8 | config.c crc64.h fmacros.h setproctitle.c stream.c udp_client.c version.h \ 9 | config.h debug.c narc.c sha1.c stream.h udp_client.h 10 | 11 | -------------------------------------------------------------------------------- /src/sha1.h: -------------------------------------------------------------------------------- 1 | /* ================ sha1.h ================ */ 2 | /* 3 | SHA-1 in C 4 | By Steve Reid 5 | 100% Public Domain 6 | */ 7 | 8 | typedef struct { 9 | u_int32_t state[5]; 10 | u_int32_t count[2]; 11 | unsigned char buffer[64]; 12 | } SHA1_CTX; 13 | 14 | void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]); 15 | void SHA1Init(SHA1_CTX* context); 16 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len); 17 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context); 18 | -------------------------------------------------------------------------------- /src/mkreleasehdr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n1` 3 | GIT_DIRTY=`git diff --no-ext-diff 2> /dev/null | wc -l` 4 | BUILD_ID=`uname -n`"-"`date +%s` 5 | test -f release.h || touch release.h 6 | (cat release.h | grep SHA1 | grep $GIT_SHA1) && \ 7 | (cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date 8 | echo "#define NARC_GIT_SHA1 \"$GIT_SHA1\"" > release.h 9 | echo "#define NARC_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h 10 | echo "#define NARC_BUILD_ID \"$BUILD_ID\"" >> release.h 11 | touch release.c # Force recompile of release.c 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![narc logo](http://nano-assets.gopagoda.io/readme-headers/narc.png)](http://nanobox.io/open-source#narc) 2 | [![Build Status](https://travis-ci.org/nanopack/narc.svg)](https://travis-ci.org/nanopack/narc) 3 | 4 | # narc 5 | 6 | Small client utility to watch log files and ship to syslog service, like logvac. 7 | 8 | ## Status 9 | 10 | Complete/Stable 11 | 12 | ## TODO 13 | 14 | - documentation 15 | 16 | ### Contributing 17 | 18 | Contributions to the narc project are welcome and encouraged. Narc is a [Nanobox](https://nanobox.io) project and contributions should follow the [Nanobox Contribution Process & Guidelines](https://docs.nanobox.io/contributing/). 19 | 20 | ### Licence 21 | 22 | Mozilla Public License Version 2.0 23 | 24 | [![open source](http://nano-assets.gopagoda.io/open-src/nanobox-open-src.png)](http://nanobox.io/open-source) 25 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # rebuild: autoreconf -fvi 2 | 3 | AC_PREREQ(2.61) 4 | 5 | AC_INIT([narcd], [0.2.2]) 6 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 7 | AC_CONFIG_SRCDIR([src/narc.c]) 8 | 9 | AC_PROG_CC 10 | 11 | AC_SEARCH_LIBS(ether_ntoa, socket, 12 | [], 13 | [AC_MSG_ERROR([socket libraries not found.])] 14 | ) 15 | 16 | AC_SEARCH_LIBS(pthread_mutexattr_init, pthread, 17 | [], 18 | [AC_MSG_ERROR([pthread libraries not found.])] 19 | ) 20 | 21 | AC_SEARCH_LIBS(inet_aton, nsl, 22 | [], 23 | [AC_MSG_ERROR([nsl libraries not found.])] 24 | ) 25 | 26 | AC_CHECK_HEADERS(uv.h, 27 | [], 28 | [AC_MSG_ERROR([libuv header files not found.]); break] 29 | ) 30 | 31 | AC_SEARCH_LIBS(uv_run, uv, 32 | [] 33 | [AC_SEARCH_LIBS(uv_run, uv, 34 | [], 35 | [AC_MSG_ERROR([libuv libraries not found.])], 36 | -lsendfile -lkstat 37 | )] 38 | ) 39 | 40 | AC_OUTPUT(Makefile src/Makefile) 41 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # determine vagrant provider 5 | ENV['VAGRANT_DEFAULT_PROVIDER'] = ENV['NANOBOX_BUILD_VAGRANT_PROVIDER'] || 'virtualbox' 6 | 7 | Vagrant.configure('2') do |config| 8 | 9 | config.vm.box = 'ubuntu/trusty64' 10 | 11 | config.vm.provider 'virtualbox' do |v| 12 | v.memory = 1024 13 | v.cpus = 1 14 | end 15 | 16 | config.vm.provider "vmware_fusion" do |v, override| 17 | v.vmx["memsize"] = "1024" 18 | v.vmx["numvcpus"] = "1" 19 | v.gui = false 20 | override.vm.box = "trusty64_vmware" 21 | override.vm.box_url = 'https://github.com/pagodabox/vagrant-packer-templates/releases/download/v0.2.0/trusty64_vmware.box' 22 | end 23 | 24 | config.vm.network "private_network", type: "dhcp" 25 | 26 | config.vm.provision "shell", inline: <<-SCRIPT 27 | echo "installing build tools..." 28 | apt-get -y update -qq 29 | apt-get -y upgrade 30 | apt-get install -y build-essential 31 | add-apt-repository ppa:wesmason/nodejs-backport -y 32 | apt-get -qq update 33 | apt-get install -y libuv1 libuv1-dev autoconf valgrind 34 | apt-get -y autoremove 35 | SCRIPT 36 | 37 | end 38 | -------------------------------------------------------------------------------- /narc.conf: -------------------------------------------------------------------------------- 1 | ########### 2 | # general # 3 | ########### 4 | 5 | daemonize no 6 | pidfile /var/run/narc.pid 7 | 8 | ########### 9 | # logging # 10 | ########### 11 | 12 | loglevel debug 13 | # logfile /var/log/narc.log 14 | syslog-enabled no 15 | syslog-ident narc 16 | syslog-facility local0 17 | 18 | ########## 19 | # server # 20 | ########## 21 | 22 | # remote syslog 23 | remote-host 192.168.157.1 24 | # remote-port 514 25 | remote-port 1234 26 | remote-proto udp 27 | 28 | # max server connect attempts 29 | max-connect-attempts 12 30 | # millisecond delay between attempts 31 | connect-retry-delay 5000 32 | 33 | ########### 34 | # streams # 35 | ########### 36 | 37 | # max file open attempts 38 | max-open-attempts 12 39 | # millisecond delay between attempts 40 | open-retry-delay 5000 41 | 42 | # identifier to prefix all messages with 43 | stream-id 123.456 44 | 45 | # syslog facility for streams 46 | stream-facility user 47 | 48 | # syslog priority for streams 49 | stream-priority error 50 | 51 | # log streams 52 | # stream apache[access] /var/log/httpd/access.log 53 | # stream apache[error] /var/log/httpd/error.log 54 | # stream php[error] /var/log/php/error.log 55 | 56 | stream test[a] /tmp/narc/a.out 57 | stream test[b] /tmp/narc/b.out -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * CDDL HEADER START 5 | * 6 | * The contents of this file are subject to the terms of the 7 | * Common Development and Distribution License (the "License"). 8 | * You may not use this file except in compliance with the License. 9 | * 10 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11 | * or http://www.opensolaris.org/os/licensing. 12 | * See the License for the specific language governing permissions 13 | * and limitations under the License. 14 | * 15 | * When distributing Covered Code, include this CDDL HEADER in each 16 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17 | * If applicable, add the following below this CDDL HEADER, with the 18 | * fields enclosed by brackets "[]" replaced with your own identifying 19 | * information: Portions Copyright [yyyy] [name of copyright owner] 20 | * 21 | * CDDL HEADER END 22 | */ 23 | /* 24 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 25 | */ 26 | 27 | #ifndef NARC_CONFIG_H 28 | #define NARC_CONFIG_H 29 | 30 | /*----------------------------------------------------------------------------- 31 | * Functions prototypes 32 | *----------------------------------------------------------------------------*/ 33 | 34 | void load_server_config(char *filename, char *options); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/udp_client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #ifndef NARC_UDP 27 | #define NARC_UDP 28 | 29 | #include "narc.h" 30 | #include "sds.h" /* dynamic safe strings */ 31 | 32 | #include /* Event driven programming library */ 33 | 34 | /* connection states */ 35 | #define NARC_UDP_INITIALIZED 0 36 | #define NARC_UDP_BOUND 1 37 | /*----------------------------------------------------------------------------- 38 | * Data types 39 | *----------------------------------------------------------------------------*/ 40 | 41 | typedef struct { 42 | int state; /* connection state */ 43 | uv_udp_t socket; /* udp socket */ 44 | uv_getaddrinfo_t resolver; 45 | struct sockaddr_in send_addr; 46 | } narc_udp_client; 47 | 48 | /*----------------------------------------------------------------------------- 49 | * Functions prototypes 50 | *----------------------------------------------------------------------------*/ 51 | 52 | /* watchers */ 53 | 54 | /* api */ 55 | void init_udp_client(void); 56 | void clean_udp_client(void); 57 | void submit_udp_message(char *message); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/fmacros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _NARC_FMACRO_H 31 | #define _NARC_FMACRO_H 32 | 33 | #define _BSD_SOURCE 34 | 35 | #if defined(__linux__) 36 | #define _GNU_SOURCE 37 | #endif 38 | 39 | #if defined(__linux__) || defined(__OpenBSD__) 40 | #define _XOPEN_SOURCE 700 41 | /* 42 | * On NetBSD, _XOPEN_SOURCE undefines _NETBSD_SOURCE and 43 | * thus hides inet_aton etc. 44 | */ 45 | #elif !defined(__NetBSD__) 46 | #define _XOPEN_SOURCE 47 | #endif 48 | 49 | #define _LARGEFILE_SOURCE 50 | #define _FILE_OFFSET_BITS 64 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __REDIS_UTIL_H 31 | #define __REDIS_UTIL_H 32 | 33 | #include "sds.h" 34 | 35 | int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase); 36 | int stringmatch(const char *p, const char *s, int nocase); 37 | long long memtoll(const char *p, int *err); 38 | int ll2string(char *s, size_t len, long long value); 39 | int string2ll(const char *s, size_t slen, long long *value); 40 | int string2l(const char *s, size_t slen, long *value); 41 | sds getAbsolutePath(char *filename); 42 | int pathIsBaseName(char *path); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/tcp_client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #ifndef NARC_TCP 27 | #define NARC_TCP 28 | 29 | #include "narc.h" 30 | #include "sds.h" /* dynamic safe strings */ 31 | 32 | #include /* Event driven programming library */ 33 | 34 | /* connection states */ 35 | #define NARC_TCP_INITIALIZED 0 36 | #define NARC_TCP_ESTABLISHED 1 37 | 38 | /*----------------------------------------------------------------------------- 39 | * Data types 40 | *----------------------------------------------------------------------------*/ 41 | 42 | typedef struct { 43 | int state; /* connection state */ 44 | uv_tcp_t *socket; /* tcp socket */ 45 | uv_stream_t *stream; /* connection stream */ 46 | int attempts; /* connection attempts */ 47 | uv_getaddrinfo_t resolver; 48 | } narc_tcp_client; 49 | 50 | /*----------------------------------------------------------------------------- 51 | * Functions prototypes 52 | *----------------------------------------------------------------------------*/ 53 | 54 | /* watchers */ 55 | void start_resolve(void); 56 | void start_tcp_connect(struct addrinfo *res); 57 | void start_tcp_read(uv_stream_t *stream); 58 | 59 | /* api */ 60 | void init_tcp_client(void); 61 | void clean_tcp_client(void); 62 | void submit_tcp_message(char *message); 63 | void start_tcp_connect_timer(void); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/solarisfixes.h: -------------------------------------------------------------------------------- 1 | /* Solaris specific fixes. 2 | * 3 | * Copyright (c) 2009-2012, 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 | #if defined(__GNUC__) 32 | #include 33 | #undef isnan 34 | #define isnan(x) \ 35 | __extension__({ __typeof (x) __x_a = (x); \ 36 | __builtin_expect(__x_a != __x_a, 0); }) 37 | 38 | #undef isfinite 39 | #define isfinite(x) \ 40 | __extension__ ({ __typeof (x) __x_f = (x); \ 41 | __builtin_expect(!isnan(__x_f - __x_f), 1); }) 42 | 43 | #undef isinf 44 | #define isinf(x) \ 45 | __extension__ ({ __typeof (x) __x_i = (x); \ 46 | __builtin_expect(!isnan(__x_i) && !isfinite(__x_i), 0); }) 47 | 48 | #define u_int uint 49 | #define u_int32_t uint32_t 50 | #endif /* __GNUC__ */ 51 | -------------------------------------------------------------------------------- /src/narcassert.h: -------------------------------------------------------------------------------- 1 | /* narcassert.h -- Drop in replacemnet assert.h that prints the stack trace 2 | * in the Narc logs. 3 | * 4 | * This file should be included instead of "assert.h" inside libraries used by 5 | * Narc that are using assertions, so instead of Narc disappearing with 6 | * SIGABORT, we get the details and stack trace inside the log file. 7 | * 8 | * ---------------------------------------------------------------------------- 9 | * 10 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 11 | * All rights reserved. 12 | * Copyright (c) 2013, Andrew Bennett 13 | * All rights reserved. 14 | * Copyright (c) 2013, Tyler Flint 15 | * All rights reserved. 16 | * 17 | * Redistribution and use in source and binary forms, with or without 18 | * modification, are permitted provided that the following conditions are met: 19 | * 20 | * * Redistributions of source code must retain the above copyright notice, 21 | * this list of conditions and the following disclaimer. 22 | * * Redistributions in binary form must reproduce the above copyright 23 | * notice, this list of conditions and the following disclaimer in the 24 | * documentation and/or other materials provided with the distribution. 25 | * * Neither the name of Redis nor the names of its contributors may be used 26 | * to endorse or promote products derived from this software without 27 | * specific prior written permission. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 30 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 33 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 | * POSSIBILITY OF SUCH DAMAGE. 40 | */ 41 | 42 | #ifndef __NARC_ASSERT_H__ 43 | #define __NARC_ASSERT_H__ 44 | 45 | #include /* for _exit() */ 46 | 47 | #define assert(_e) ((_e)?(void)0 : (_narcAssert(#_e,__FILE__,__LINE__),_exit(1))) 48 | 49 | void _narcAssert(char *estr, char *file, int line); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/endianconv.h: -------------------------------------------------------------------------------- 1 | /* See endianconv.c top comments for more information 2 | * 3 | * ---------------------------------------------------------------------------- 4 | * 5 | * Copyright (c) 2011-2012, 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 __ENDIANCONV_H 34 | #define __ENDIANCONV_H 35 | 36 | #include "config.h" 37 | #include 38 | 39 | void memrev16(void *p); 40 | void memrev32(void *p); 41 | void memrev64(void *p); 42 | uint16_t intrev16(uint16_t v); 43 | uint32_t intrev32(uint32_t v); 44 | uint64_t intrev64(uint64_t v); 45 | 46 | /* variants of the function doing the actual convertion only if the target 47 | * host is big endian */ 48 | #if (BYTE_ORDER == LITTLE_ENDIAN) 49 | #define memrev16ifbe(p) 50 | #define memrev32ifbe(p) 51 | #define memrev64ifbe(p) 52 | #define intrev16ifbe(v) (v) 53 | #define intrev32ifbe(v) (v) 54 | #define intrev64ifbe(v) (v) 55 | #else 56 | #define memrev16ifbe(p) memrev16(p) 57 | #define memrev32ifbe(p) memrev32(p) 58 | #define memrev64ifbe(p) memrev64(p) 59 | #define intrev16ifbe(v) intrev16(v) 60 | #define intrev32ifbe(v) intrev32(v) 61 | #define intrev64ifbe(v) intrev64(v) 62 | #endif 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/stream.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | #ifndef NARC_STREAM 26 | #define NARC_STREAM 27 | 28 | #include "narc.h" 29 | #include 30 | 31 | /* Stream locking */ 32 | #define NARC_STREAM_LOCKED 1 33 | #define NARC_STREAM_UNLOCKED 2 34 | #define NARC_STREAM_BUFFERS 1 35 | 36 | /*----------------------------------------------------------------------------- 37 | * Data types 38 | *----------------------------------------------------------------------------*/ 39 | 40 | typedef struct { 41 | char *id; /* message id prefix */ 42 | char *file; /* absolute path to the file */ 43 | int fd; /* file descriptor */ 44 | off_t size; /* last known file size in bytes */ 45 | uv_buf_t buffer[NARC_STREAM_BUFFERS]; /* read buffer (file content) */ 46 | char line[(NARC_MAX_LOGMSG_LEN + 1) * 2]; /* the current and previous lines buffer */ 47 | char *current_line; /* current line */ 48 | char *previous_line; /* previous line */ 49 | int repeat_count; /* how many times the previous line was repeated */ 50 | int index; /* the line character index */ 51 | int lock; /* read lock to prevent resetting buffers */ 52 | int attempts; /* open attempts */ 53 | int rate_count; /* */ 54 | int missed_count; /* */ 55 | int message_header_size; 56 | int64_t offset; 57 | int truncate; 58 | uv_fs_event_t *fs_events; 59 | uv_timer_t *open_timer; 60 | } narc_stream; 61 | 62 | /*----------------------------------------------------------------------------- 63 | * Functions prototypes 64 | *----------------------------------------------------------------------------*/ 65 | 66 | /* watchers */ 67 | void start_file_open(narc_stream *stream); 68 | void start_file_watcher(narc_stream *stream); 69 | void start_file_open_timer(narc_stream *stream); 70 | void start_file_stat(narc_stream *stream); 71 | void start_file_read(narc_stream *stream); 72 | void start_rate_limit_timer(narc_stream *stream); 73 | 74 | /* api */ 75 | narc_stream *new_stream(char *id, char *file); 76 | void free_stream(void *ptr); 77 | void init_stream(narc_stream *stream); 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/adlist.h: -------------------------------------------------------------------------------- 1 | /* adlist.h - A generic doubly linked list implementation 2 | * 3 | * Copyright (c) 2006-2012, 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 __ADLIST_H__ 32 | #define __ADLIST_H__ 33 | 34 | /* Node, List, and Iterator are the only data structures used currently. */ 35 | 36 | typedef struct listNode { 37 | struct listNode *prev; 38 | struct listNode *next; 39 | void *value; 40 | } listNode; 41 | 42 | typedef struct listIter { 43 | listNode *next; 44 | int direction; 45 | } listIter; 46 | 47 | typedef struct list { 48 | listNode *head; 49 | listNode *tail; 50 | void *(*dup)(void *ptr); 51 | void (*free)(void *ptr); 52 | int (*match)(void *ptr, void *key); 53 | unsigned long len; 54 | } list; 55 | 56 | /* Functions implemented as macros */ 57 | #define listLength(l) ((l)->len) 58 | #define listFirst(l) ((l)->head) 59 | #define listLast(l) ((l)->tail) 60 | #define listPrevNode(n) ((n)->prev) 61 | #define listNextNode(n) ((n)->next) 62 | #define listNodeValue(n) ((n)->value) 63 | 64 | #define listSetDupMethod(l,m) ((l)->dup = (m)) 65 | #define listSetFreeMethod(l,m) ((l)->free = (m)) 66 | #define listSetMatchMethod(l,m) ((l)->match = (m)) 67 | 68 | #define listGetDupMethod(l) ((l)->dup) 69 | #define listGetFree(l) ((l)->free) 70 | #define listGetMatchMethod(l) ((l)->match) 71 | 72 | /* Prototypes */ 73 | list *listCreate(void); 74 | void listRelease(list *list); 75 | list *listAddNodeHead(list *list, void *value); 76 | list *listAddNodeTail(list *list, void *value); 77 | list *listInsertNode(list *list, listNode *old_node, void *value, int after); 78 | void listDelNode(list *list, listNode *node); 79 | listIter *listGetIterator(list *list, int direction); 80 | listNode *listNext(listIter *iter); 81 | void listReleaseIterator(listIter *iter); 82 | list *listDup(list *orig); 83 | listNode *listSearchKey(list *list, void *key); 84 | listNode *listIndex(list *list, long index); 85 | void listRewind(list *list, listIter *li); 86 | void listRewindTail(list *list, listIter *li); 87 | void listRotate(list *list); 88 | 89 | /* Directions for iterators */ 90 | #define AL_START_HEAD 0 91 | #define AL_START_TAIL 1 92 | 93 | #endif /* __ADLIST_H__ */ 94 | -------------------------------------------------------------------------------- /src/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 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 __SDS_H 32 | #define __SDS_H 33 | 34 | #define SDS_MAX_PREALLOC (1024*1024) 35 | 36 | #include 37 | #include 38 | 39 | typedef char *sds; 40 | 41 | struct sdshdr { 42 | int len; 43 | int free; 44 | char buf[]; 45 | }; 46 | 47 | static inline size_t sdslen(const sds s) { 48 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 49 | return sh->len; 50 | } 51 | 52 | static inline size_t sdsavail(const sds s) { 53 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 54 | return sh->free; 55 | } 56 | 57 | sds sdsnewlen(const void *init, size_t initlen); 58 | sds sdsnew(const char *init); 59 | sds sdsempty(void); 60 | size_t sdslen(const sds s); 61 | sds sdsdup(const sds s); 62 | void sdsfree(sds s); 63 | size_t sdsavail(const sds s); 64 | sds sdsgrowzero(sds s, size_t len); 65 | sds sdscatlen(sds s, const void *t, size_t len); 66 | sds sdscat(sds s, const char *t); 67 | sds sdscatsds(sds s, const sds t); 68 | sds sdscpylen(sds s, const char *t, size_t len); 69 | sds sdscpy(sds s, const char *t); 70 | 71 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 72 | #ifdef __GNUC__ 73 | sds sdscatprintf(sds s, const char *fmt, ...) 74 | __attribute__((format(printf, 2, 3))); 75 | #else 76 | sds sdscatprintf(sds s, const char *fmt, ...); 77 | #endif 78 | 79 | sds sdstrim(sds s, const char *cset); 80 | void sdsrange(sds s, int start, int end); 81 | void sdsupdatelen(sds s); 82 | void sdsclear(sds s); 83 | int sdscmp(const sds s1, const sds s2); 84 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); 85 | void sdsfreesplitres(sds *tokens, int count); 86 | void sdstolower(sds s); 87 | void sdstoupper(sds s); 88 | sds sdsfromlonglong(long long value); 89 | sds sdscatrepr(sds s, const char *p, size_t len); 90 | sds *sdssplitargs(const char *line, int *argc); 91 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); 92 | sds sdsjoin(char **argv, int argc, char *sep); 93 | 94 | /* Low level functions exposed to the user API */ 95 | sds sdsMakeRoomFor(sds s, size_t addlen); 96 | void sdsIncrLen(sds s, int incr); 97 | sds sdsRemoveFreeSpace(sds s); 98 | size_t sdsAllocSize(sds s); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/endianconv.c: -------------------------------------------------------------------------------- 1 | /* endinconv.c -- Endian conversions utilities. 2 | * 3 | * This functions are never called directly, but always using the macros 4 | * defined into endianconv.h, this way we define everything is a non-operation 5 | * if the arch is already little endian. 6 | * 7 | * Redis tries to encode everything as little endian (but a few things that need 8 | * to be backward compatible are still in big endian) because most of the 9 | * production environments are little endian, and we have a lot of conversions 10 | * in a few places because ziplists, intsets, zipmaps, need to be endian-neutral 11 | * even in memory, since they are serialied on RDB files directly with a single 12 | * write(2) without other additional steps. 13 | * 14 | * ---------------------------------------------------------------------------- 15 | * 16 | * Copyright (c) 2011-2012, Salvatore Sanfilippo 17 | * All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions are met: 21 | * 22 | * * Redistributions of source code must retain the above copyright notice, 23 | * this list of conditions and the following disclaimer. 24 | * * Redistributions in binary form must reproduce the above copyright 25 | * notice, this list of conditions and the following disclaimer in the 26 | * documentation and/or other materials provided with the distribution. 27 | * * Neither the name of Redis nor the names of its contributors may be used 28 | * to endorse or promote products derived from this software without 29 | * specific prior written permission. 30 | * 31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 32 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 35 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 36 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 39 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 | * POSSIBILITY OF SUCH DAMAGE. 42 | */ 43 | 44 | 45 | #include 46 | 47 | /* Toggle the 16 bit unsigned integer pointed by *p from little endian to 48 | * big endian */ 49 | void memrev16(void *p) { 50 | unsigned char *x = p, t; 51 | 52 | t = x[0]; 53 | x[0] = x[1]; 54 | x[1] = t; 55 | } 56 | 57 | /* Toggle the 32 bit unsigned integer pointed by *p from little endian to 58 | * big endian */ 59 | void memrev32(void *p) { 60 | unsigned char *x = p, t; 61 | 62 | t = x[0]; 63 | x[0] = x[3]; 64 | x[3] = t; 65 | t = x[1]; 66 | x[1] = x[2]; 67 | x[2] = t; 68 | } 69 | 70 | /* Toggle the 64 bit unsigned integer pointed by *p from little endian to 71 | * big endian */ 72 | void memrev64(void *p) { 73 | unsigned char *x = p, t; 74 | 75 | t = x[0]; 76 | x[0] = x[7]; 77 | x[7] = t; 78 | t = x[1]; 79 | x[1] = x[6]; 80 | x[6] = t; 81 | t = x[2]; 82 | x[2] = x[5]; 83 | x[5] = t; 84 | t = x[3]; 85 | x[3] = x[4]; 86 | x[4] = t; 87 | } 88 | 89 | uint16_t intrev16(uint16_t v) { 90 | memrev16(&v); 91 | return v; 92 | } 93 | 94 | uint32_t intrev32(uint32_t v) { 95 | memrev32(&v); 96 | return v; 97 | } 98 | 99 | uint64_t intrev64(uint64_t v) { 100 | memrev64(&v); 101 | return v; 102 | } 103 | 104 | #ifdef TESTMAIN 105 | #include 106 | 107 | int main(void) { 108 | char buf[32]; 109 | 110 | sprintf(buf,"ciaoroma"); 111 | memrev16(buf); 112 | printf("%s\n", buf); 113 | 114 | sprintf(buf,"ciaoroma"); 115 | memrev32(buf); 116 | printf("%s\n", buf); 117 | 118 | sprintf(buf,"ciaoroma"); 119 | memrev64(buf); 120 | printf("%s\n", buf); 121 | 122 | return 0; 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /src/crc16.c: -------------------------------------------------------------------------------- 1 | #include "narc.h" 2 | 3 | /* 4 | * Copyright 2001-2010 Georges Menie (www.menie.org) 5 | * Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style) 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 12 | * notice, 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 the University of California, Berkeley nor the 17 | * names of its contributors may be used to endorse or promote products 18 | * derived from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 21 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 24 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /* CRC16 implementation according to CCITT standards. 33 | * 34 | * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the 35 | * following parameters: 36 | * 37 | * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" 38 | * Width : 16 bit 39 | * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) 40 | * Initialization : 0000 41 | * Reflect Input byte : False 42 | * Reflect Output CRC : False 43 | * Xor constant to output CRC : 0000 44 | * Output for "123456789" : 31C3 45 | */ 46 | 47 | static const uint16_t crc16tab[256]= { 48 | 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 49 | 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 50 | 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 51 | 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 52 | 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 53 | 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 54 | 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 55 | 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 56 | 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 57 | 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 58 | 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 59 | 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 60 | 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 61 | 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 62 | 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 63 | 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 64 | 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 65 | 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 66 | 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 67 | 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 68 | 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 69 | 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 70 | 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 71 | 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 72 | 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 73 | 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 74 | 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 75 | 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 76 | 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 77 | 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 78 | 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 79 | 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 80 | }; 81 | 82 | uint16_t crc16(const char *buf, int len) { 83 | int counter; 84 | uint16_t crc = 0; 85 | for (counter = 0; counter < len; counter++) 86 | crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; 87 | return crc; 88 | } 89 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * Copyright (c) 2013, Andrew Bennett 5 | * All rights reserved. 6 | * Copyright (c) 2013, Tyler Flint 7 | * All rights reserved. 8 | * 9 | * Contribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * * Contributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * * Contributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * * Neither the name of Hooky nor the names of its contributors may be used 18 | * to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "narc.h" 35 | #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */ 36 | #include "crc64.h" 37 | 38 | #include 39 | #include 40 | 41 | #ifdef HAVE_BACKTRACE 42 | #include 43 | #include 44 | #include 45 | #endif /* HAVE_BACKTRACE */ 46 | 47 | /* ================================= Debugging ============================== */ 48 | 49 | /* Compute the sha1 of string at 's' with 'len' bytes long. 50 | * The SHA1 is then xored against the string pointed by digest. 51 | * Since xor is commutative, this operation is used in order to 52 | * "add" digests relative to unordered elements. 53 | * 54 | * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ 55 | void xorDigest(unsigned char *digest, void *ptr, size_t len) { 56 | SHA1_CTX ctx; 57 | unsigned char hash[20], *s = ptr; 58 | int j; 59 | 60 | SHA1Init(&ctx); 61 | SHA1Update(&ctx,s,len); 62 | SHA1Final(hash,&ctx); 63 | 64 | for (j = 0; j < 20; j++) 65 | digest[j] ^= hash[j]; 66 | } 67 | 68 | /* This function instead of just computing the SHA1 and xoring it 69 | * against digest, also perform the digest of "digest" itself and 70 | * replace the old value with the new one. 71 | * 72 | * So the final digest will be: 73 | * 74 | * digest = SHA1(digest xor SHA1(data)) 75 | * 76 | * This function is used every time we want to preserve the order so 77 | * that digest(a,b,c,d) will be different than digest(b,c,d,a) 78 | * 79 | * Also note that mixdigest("foo") followed by mixdigest("bar") 80 | * will lead to a different digest compared to "fo", "obar". 81 | */ 82 | void mixDigest(unsigned char *digest, void *ptr, size_t len) { 83 | SHA1_CTX ctx; 84 | char *s = ptr; 85 | 86 | xorDigest(digest,s,len); 87 | SHA1Init(&ctx); 88 | SHA1Update(&ctx,digest,20); 89 | SHA1Final(digest,&ctx); 90 | } 91 | 92 | /* =========================== Crash handling ============================== */ 93 | 94 | void _narcAssert(char *estr, char *file, int line) { 95 | narc_log(NARC_WARNING,"=== ASSERTION FAILED ==="); 96 | narc_log(NARC_WARNING,"==> %s:%d '%s' is not true",file,line,estr); 97 | #ifdef HAVE_BACKTRACE 98 | server.assert_failed = estr; 99 | server.assert_file = file; 100 | server.assert_line = line; 101 | narc_log(NARC_WARNING,"(forcing SIGSEGV to print the bug report.)"); 102 | #endif 103 | *((char*)-1) = 'x'; 104 | } 105 | 106 | void _narcPanic(char *msg, char *file, int line) { 107 | narc_log(NARC_WARNING,"------------------------------------------------"); 108 | narc_log(NARC_WARNING,"!!! Software Failure. Press left mouse button to continue"); 109 | narc_log(NARC_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line); 110 | #ifdef HAVE_BACKTRACE 111 | narc_log(NARC_WARNING,"(forcing SIGSEGV in order to print the stack trace)"); 112 | #endif 113 | narc_log(NARC_WARNING,"------------------------------------------------"); 114 | *((char*)-1) = 'x'; 115 | } 116 | -------------------------------------------------------------------------------- /src/udp_client.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #include "narc.h" 27 | #include "udp_client.h" 28 | 29 | #include "sds.h" /* dynamic safe strings */ 30 | // #include "malloc.h" /* total memory usage aware version of malloc/free */ 31 | 32 | #include /* standard buffered input/output */ 33 | #include /* standard library definitions */ 34 | #include /* standard symbolic constants and types */ 35 | #include /* Event driven programming library */ 36 | #include 37 | 38 | /*============================ Utility functions ============================ */ 39 | 40 | narc_udp_client 41 | *new_udp_client(void) 42 | { 43 | narc_udp_client *client = (narc_udp_client *)malloc(sizeof(narc_udp_client)); 44 | memset(client, 0, sizeof(narc_udp_client)); 45 | return client; 46 | } 47 | 48 | void 49 | handle_udp_read_alloc_buffer(uv_handle_t *handle, size_t len, struct uv_buf_t *buf) 50 | { 51 | buf->base = malloc(len); 52 | buf->len = len; 53 | } 54 | 55 | // uv_buf_t 56 | // handle_udp_read_alloc_buffer(uv_handle_t* handle, size_t size) 57 | // { 58 | // return uv_buf_init(malloc(size), size); 59 | // } 60 | 61 | void 62 | handle_udp_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) 63 | { 64 | if (nread >= 0){ 65 | narc_log(NARC_WARNING, "dropping packet: %s", buf->base); 66 | 67 | }else { 68 | narc_log(NARC_WARNING, "Udp read error: %s", 69 | uv_strerror(nread)); 70 | } 71 | if (buf->base) 72 | free(buf->base); 73 | } 74 | 75 | void 76 | handle_udp_send(uv_udp_send_t* req, int status) 77 | { 78 | if (status != 0){ 79 | narc_log(NARC_WARNING, "Udp send error: %s", 80 | uv_err_name(status)); 81 | } 82 | uv_buf_t *buf = (uv_buf_t *)req->data; 83 | // narc_log(NARC_WARNING, "message again: %s", buf->base); 84 | sdsfree(buf->base); 85 | free(buf); 86 | free(req); 87 | } 88 | 89 | void 90 | start_udp_read() 91 | { 92 | narc_udp_client *client = (narc_udp_client *)server.client; 93 | uv_udp_recv_start(&client->socket, handle_udp_read_alloc_buffer, handle_udp_read); 94 | } 95 | 96 | void start_udp_bind(struct addrinfo *res); 97 | 98 | void 99 | handle_udp_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) 100 | { 101 | if (status >= 0){ 102 | start_udp_bind(res); 103 | }else{ 104 | narc_log(NARC_WARNING, "server did not resolve: %s", server.host); 105 | } 106 | } 107 | 108 | /*=============================== Watchers ================================== */ 109 | 110 | void 111 | start_udp_resolve(void) 112 | { 113 | struct addrinfo hints; 114 | hints.ai_family = PF_INET; 115 | hints.ai_socktype = SOCK_STREAM; 116 | hints.ai_protocol = IPPROTO_TCP; 117 | hints.ai_flags = 0; 118 | narc_log(NARC_WARNING, "server resolving: %s", server.host); 119 | narc_udp_client *client = (narc_udp_client *)server.client; 120 | 121 | uv_getaddrinfo(server.loop, &client->resolver, handle_udp_resolved, server.host, "80", &hints); 122 | } 123 | 124 | void 125 | start_udp_bind(struct addrinfo *res) 126 | { 127 | 128 | narc_udp_client *client = (narc_udp_client *)server.client; 129 | 130 | memcpy(&client->send_addr,res->ai_addr,sizeof(res->ai_addr)), 131 | client->send_addr.sin_port = htons(server.port); 132 | narc_log(NARC_WARNING, "server resolved: '%s' to %s:%d", server.host, inet_ntoa(client->send_addr.sin_addr),ntohs(client->send_addr.sin_port)); 133 | 134 | uv_udp_init(server.loop, &client->socket); 135 | 136 | struct sockaddr_in recv_addr; 137 | uv_ip4_addr("0.0.0.0", 0, &recv_addr); 138 | uv_udp_bind(&client->socket, (struct sockaddr *)&recv_addr, 0); 139 | 140 | client->state = NARC_UDP_BOUND; 141 | start_udp_read(); 142 | 143 | uv_freeaddrinfo(res); 144 | } 145 | 146 | /*================================== API ==================================== */ 147 | 148 | void 149 | init_udp_client(void) 150 | { 151 | narc_udp_client *client = new_udp_client(); 152 | client->state = NARC_UDP_INITIALIZED; 153 | 154 | server.client = (void *)client; 155 | start_udp_resolve(); 156 | } 157 | 158 | void 159 | clean_udp_client(void) 160 | { 161 | narc_udp_client *client = (narc_udp_client *)server.client; 162 | // uv_udp_recv_stop((uv_udp_t *)&client->socket); 163 | uv_close((uv_handle_t *)&client->socket, NULL); 164 | // server.client = NULL; 165 | } 166 | 167 | void 168 | submit_udp_message(char *message) 169 | { 170 | if (server.client == NULL) { 171 | sdsfree(message); 172 | return; 173 | } 174 | narc_udp_client *client = (narc_udp_client *)server.client; 175 | int len = strlen(message); 176 | if (client->state == NARC_UDP_BOUND && len > 2) { 177 | 178 | // we make the packet one character less so that we aren't sending the newline character 179 | message[strlen(message)-1] = '\0'; 180 | // narc_log(NARC_WARNING, "sockaddr: %d %d %d %p", client->send_addr.sin_family, client->send_addr.sin_port, client->send_addr.sin_addr.s_addr, &client->send_addr); 181 | uv_udp_send_t *req = (uv_udp_send_t *)malloc(sizeof(uv_udp_send_t)); 182 | memset(req, 0, sizeof(uv_udp_send_t)); 183 | uv_buf_t *buf = malloc(sizeof(uv_buf_t)); 184 | memset(buf, 0, sizeof(uv_buf_t)); 185 | 186 | *buf = uv_buf_init(message, strlen(message)); 187 | req->data = (void *)buf; 188 | // narc_udp_client *client = (narc_udp_client *)server.client; 189 | uv_udp_send(req, &client->socket, buf, 1, (struct sockaddr *)&client->send_addr, handle_udp_send); 190 | } else { 191 | sdsfree(message); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/setproctitle.c: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * setproctitle.c - Linux/Darwin setproctitle. 3 | * -------------------------------------------------------------------------- 4 | * Copyright (C) 2010 William Ahern 5 | * Copyright (C) 2013 Salvatore Sanfilippo 6 | * Copyright (C) 2013 Stam He 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to permit 13 | * persons to whom the Software is furnished to do so, subject to the 14 | * following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included 17 | * in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 22 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 23 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | * ========================================================================== 27 | */ 28 | #ifndef _GNU_SOURCE 29 | #define _GNU_SOURCE 30 | #endif 31 | 32 | #include /* NULL size_t */ 33 | #include /* va_list va_start va_end */ 34 | #include /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */ 35 | #include /* vsnprintf(3) snprintf(3) */ 36 | 37 | #include /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */ 38 | 39 | #include /* errno program_invocation_name program_invocation_short_name */ 40 | 41 | #if !defined(HAVE_SETPROCTITLE) 42 | #define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) 43 | #endif 44 | 45 | 46 | #if !HAVE_SETPROCTITLE 47 | #if (defined __linux || defined __APPLE__) 48 | 49 | extern char **environ; 50 | 51 | static struct { 52 | /* original value */ 53 | const char *arg0; 54 | 55 | /* title space available */ 56 | char *base, *end; 57 | 58 | /* pointer to original nul character within base */ 59 | char *nul; 60 | 61 | _Bool reset; 62 | int error; 63 | } SPT; 64 | 65 | 66 | #ifndef SPT_MIN 67 | #define SPT_MIN(a, b) (((a) < (b))? (a) : (b)) 68 | #endif 69 | 70 | static inline size_t spt_min(size_t a, size_t b) { 71 | return SPT_MIN(a, b); 72 | } /* spt_min() */ 73 | 74 | 75 | /* 76 | * For discussion on the portability of the various methods, see 77 | * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html 78 | */ 79 | static int spt_clearenv(void) { 80 | #if __GLIBC__ 81 | clearenv(); 82 | 83 | return 0; 84 | #else 85 | extern char **environ; 86 | static char **tmp; 87 | 88 | if (!(tmp = malloc(sizeof *tmp))) 89 | return errno; 90 | 91 | tmp[0] = NULL; 92 | environ = tmp; 93 | 94 | return 0; 95 | #endif 96 | } /* spt_clearenv() */ 97 | 98 | 99 | static int spt_copyenv(char *oldenv[]) { 100 | extern char **environ; 101 | char *eq; 102 | int i, error; 103 | 104 | if (environ != oldenv) 105 | return 0; 106 | 107 | if ((error = spt_clearenv())) 108 | goto error; 109 | 110 | for (i = 0; oldenv[i]; i++) { 111 | if (!(eq = strchr(oldenv[i], '='))) 112 | continue; 113 | 114 | *eq = '\0'; 115 | error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0; 116 | *eq = '='; 117 | 118 | if (error) 119 | goto error; 120 | } 121 | 122 | return 0; 123 | error: 124 | environ = oldenv; 125 | 126 | return error; 127 | } /* spt_copyenv() */ 128 | 129 | 130 | static int spt_copyargs(int argc, char *argv[]) { 131 | char *tmp; 132 | int i; 133 | 134 | for (i = 1; i < argc || (i >= argc && argv[i]); i++) { 135 | if (!argv[i]) 136 | continue; 137 | 138 | if (!(tmp = strdup(argv[i]))) 139 | return errno; 140 | 141 | argv[i] = tmp; 142 | } 143 | 144 | return 0; 145 | } /* spt_copyargs() */ 146 | 147 | 148 | void spt_init(int argc, char *argv[]) { 149 | char **envp = environ; 150 | char *base, *end, *nul, *tmp; 151 | int i, error; 152 | 153 | if (!(base = argv[0])) 154 | return; 155 | 156 | nul = &base[strlen(base)]; 157 | end = nul + 1; 158 | 159 | for (i = 0; i < argc || (i >= argc && argv[i]); i++) { 160 | if (!argv[i] || argv[i] < end) 161 | continue; 162 | 163 | end = argv[i] + strlen(argv[i]) + 1; 164 | } 165 | 166 | for (i = 0; envp[i]; i++) { 167 | if (envp[i] < end) 168 | continue; 169 | 170 | end = envp[i] + strlen(envp[i]) + 1; 171 | } 172 | 173 | if (!(SPT.arg0 = strdup(argv[0]))) 174 | goto syerr; 175 | 176 | #if __GLIBC__ 177 | if (!(tmp = strdup(program_invocation_name))) 178 | goto syerr; 179 | 180 | program_invocation_name = tmp; 181 | 182 | if (!(tmp = strdup(program_invocation_short_name))) 183 | goto syerr; 184 | 185 | program_invocation_short_name = tmp; 186 | #elif __APPLE__ 187 | if (!(tmp = strdup(getprogname()))) 188 | goto syerr; 189 | 190 | setprogname(tmp); 191 | #endif 192 | 193 | 194 | if ((error = spt_copyenv(envp))) 195 | goto error; 196 | 197 | if ((error = spt_copyargs(argc, argv))) 198 | goto error; 199 | 200 | SPT.nul = nul; 201 | SPT.base = base; 202 | SPT.end = end; 203 | 204 | return; 205 | syerr: 206 | error = errno; 207 | error: 208 | SPT.error = error; 209 | } /* spt_init() */ 210 | 211 | 212 | #ifndef SPT_MAXTITLE 213 | #define SPT_MAXTITLE 255 214 | #endif 215 | 216 | void setproctitle(const char *fmt, ...) { 217 | char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */ 218 | va_list ap; 219 | char *nul; 220 | int len, error; 221 | 222 | if (!SPT.base) 223 | return; 224 | 225 | if (fmt) { 226 | va_start(ap, fmt); 227 | len = vsnprintf(buf, sizeof buf, fmt, ap); 228 | va_end(ap); 229 | } else { 230 | len = snprintf(buf, sizeof buf, "%s", SPT.arg0); 231 | } 232 | 233 | if (len <= 0) 234 | { error = errno; goto error; } 235 | 236 | if (!SPT.reset) { 237 | memset(SPT.base, 0, SPT.end - SPT.base); 238 | SPT.reset = 1; 239 | } else { 240 | memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base)); 241 | } 242 | 243 | len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); 244 | memcpy(SPT.base, buf, len); 245 | nul = &SPT.base[len]; 246 | 247 | if (nul < SPT.nul) { 248 | *SPT.nul = '.'; 249 | } else if (nul == SPT.nul && &nul[1] < SPT.end) { 250 | *SPT.nul = ' '; 251 | *++nul = '\0'; 252 | } 253 | 254 | return; 255 | error: 256 | SPT.error = error; 257 | } /* setproctitle() */ 258 | 259 | 260 | #endif /* __linux || __APPLE__ */ 261 | #endif /* !HAVE_SETPROCTITLE */ 262 | -------------------------------------------------------------------------------- /src/narc.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #ifndef NARC_H 27 | #define NARC_H 28 | 29 | #include /* definitions for system error logging */ 30 | 31 | #if defined(__sun) 32 | #include "solarisfixes.h" 33 | #endif 34 | 35 | #include "adlist.h" /* Linked lists */ 36 | #include "version.h" /* Version macro */ 37 | 38 | #include /* Event driven programming library */ 39 | 40 | /* Error codes */ 41 | #define NARC_OK 0 42 | #define NARC_ERR -1 43 | 44 | /* protocol types */ 45 | #define NARC_PROTO_UDP 1 46 | #define NARC_PROTO_TCP 2 47 | #define NARC_PROTO_SYSLOG 3 48 | 49 | /* Static narc configuration */ 50 | #define NARC_MAX_BUFF_SIZE 4096 51 | #define NARC_MAX_MESSAGE_SIZE 1024 52 | #define NARC_CONFIGLINE_MAX 1024 53 | #define NARC_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */ 54 | #define NARC_DEFAULT_DAEMONIZE 0 55 | #define NARC_DEFAULT_PIDFILE "/var/run/narc.pid" 56 | #define NARC_DEFAULT_LOGFILE "" 57 | #define NARC_DEFAULT_SYSLOG_IDENT "narc" 58 | #define NARC_DEFAULT_SYSLOG_ENABLED 0 59 | #define NARC_DEFAULT_HOST "127.0.0.1" 60 | #define NARC_DEFAULT_PORT 514 61 | #define NARC_DEFAULT_PROTO 2 62 | #define NARC_DEFAULT_STREAM_ID "" 63 | #define NARC_DEFAULT_STREAM_FACILITY LOG_USER 64 | #define NARC_DEFAULT_STREAM_PRIORITY LOG_ERR 65 | #define NARC_DEFAULT_OPEN_ATTEMPTS 2 66 | #define NARC_DEFAULT_OPEN_DELAY 3000 67 | #define NARC_DEFAULT_CONNECT_ATTEMPTS 2 68 | #define NARC_DEFAULT_CONNECT_DELAY 3000 69 | #define NARC_DEFAULT_RATE_LIMIT 100 70 | #define NARC_DEFAULT_RATE_TIME 10 71 | #define NARC_DEFAULT_TRUNCATE_LIMIT 1024*1024*32 /* Default truncate files when they get to 32MB */ 72 | 73 | /* Log levels */ 74 | #define NARC_DEBUG 0 75 | #define NARC_VERBOSE 1 76 | #define NARC_NOTICE 2 77 | #define NARC_WARNING 3 78 | #define NARC_LOG_RAW (1<<10) /* Modifier to log without timestamp */ 79 | // #define NARC_DEFAULT_VERBOSITY NARC_NOTICE 80 | #define NARC_DEFAULT_VERBOSITY NARC_DEBUG 81 | 82 | /* Anti-warning macro... */ 83 | #define NARC_NOTUSED(V) ((void) V) 84 | 85 | /* We can print the stacktrace, so our assert is defined this way: */ 86 | #define narcAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_narcAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1))) 87 | #define narcAssert(_e) ((_e)?(void)0 : (_narcAssert(#_e,__FILE__,__LINE__),_exit(1))) 88 | #define narcPanic(_e) _narcPanic(#_e,__FILE__,__LINE__),_exit(1) 89 | 90 | /*----------------------------------------------------------------------------- 91 | * Global state 92 | *----------------------------------------------------------------------------*/ 93 | 94 | struct narc_server { 95 | /* General */ 96 | char *pidfile; /* PID file path */ 97 | int arch_bits; /* 32 or 64 depending on sizeof(long) */ 98 | uv_loop_t *loop; /* Event loop */ 99 | 100 | /* Configuration */ 101 | int verbosity; /* Loglevel in narc.conf */ 102 | int daemonize; /* True if running as a daemon */ 103 | 104 | /* Logging */ 105 | char *logfile; /* Path of log file */ 106 | int syslog_enabled; /* Is syslog enabled? */ 107 | char *syslog_ident; /* Syslog ident */ 108 | int syslog_facility; /* Syslog facility */ 109 | 110 | /* File access */ 111 | int max_open_attempts; /* Max open attempts */ 112 | uint64_t open_retry_delay; /* Millesecond delay between attempts */ 113 | 114 | /* Server connection */ 115 | char *host; /* Remote syslog host */ 116 | int port; /* Remote syslog port */ 117 | int protocol; /* Protocol to use when communicating with remote host */ 118 | void *client; /* the client data pointer */ 119 | int max_connect_attempts; /* Max connect attempts */ 120 | uint64_t connect_retry_delay; /* Millesecond delay between attempts */ 121 | 122 | /* Streams */ 123 | list *streams; /* Stream list */ 124 | char *stream_id; /* prefix all messages */ 125 | int stream_facility; /* Syslog stream facility */ 126 | int stream_priority; /* Syslog stream priority */ 127 | int rate_limit; /* log rate limit */ 128 | int rate_time; /* log rate time */ 129 | int truncate_limit; /* size limit for truncating */ 130 | 131 | /* Time of day */ 132 | uv_timer_t time_timer; /* runs ever hald second to update the current time */ 133 | char time[16]; /* current time of day */ 134 | }; 135 | 136 | /*----------------------------------------------------------------------------- 137 | * Extern declarations 138 | *----------------------------------------------------------------------------*/ 139 | 140 | extern struct narc_server server; 141 | 142 | /*----------------------------------------------------------------------------- 143 | * Functions prototypes 144 | *----------------------------------------------------------------------------*/ 145 | /* Core functions and callbacks */ 146 | void handle_message(char *id, char *message); 147 | void narc_out_of_memory_handler(size_t allocation_size); 148 | int main(int argc, char **argv); 149 | void init_server_config(void); 150 | void init_server(void); 151 | void stop(void); 152 | 153 | /* Logging */ 154 | #ifdef __GNUC__ 155 | void narc_log(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); 156 | #else 157 | void narc_log(int level, const char *fmt, ...); 158 | #endif 159 | void narc_logRaw(int level, const char *msg); 160 | 161 | /* Git SHA1 */ 162 | char *narc_git_sha1(void); 163 | char *narc_git_dirty(void); 164 | uint64_t narc_build_id(void); 165 | 166 | // /* Deprecated */ 167 | // #if defined(__GNUC__) 168 | // void *calloc(size_t count, size_t size) __attribute__ ((deprecated)); 169 | // void free(void *ptr) __attribute__ ((deprecated)); 170 | // void *malloc(size_t size) __attribute__ ((deprecated)); 171 | // void *realloc(void *ptr, size_t size) __attribute__ ((deprecated)); 172 | // #endif 173 | 174 | /* Debugging stuff */ 175 | void _narcAssert(char *estr, char *file, int line); 176 | void _narcPanic(char *msg, char *file, int line); 177 | 178 | #endif 179 | -------------------------------------------------------------------------------- /src/tcp_client.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #include "narc.h" 27 | #include "tcp_client.h" 28 | 29 | #include "sds.h" /* dynamic safe strings */ 30 | // #include "malloc.h" /* total memory usage aware version of malloc/free */ 31 | 32 | #include /* standard buffered input/output */ 33 | #include /* standard library definitions */ 34 | #include /* standard symbolic constants and types */ 35 | #include /* Event driven programming library */ 36 | #include /* string operations */ 37 | 38 | /*============================ Utility functions ============================ */ 39 | 40 | void 41 | free_tcp_write_req(uv_write_t *req) 42 | { 43 | sdsfree((char *)req->data); 44 | free(req->bufs); 45 | free(req); 46 | } 47 | 48 | narc_tcp_client 49 | *new_tcp_client(void) 50 | { 51 | narc_tcp_client *client = (narc_tcp_client *)malloc(sizeof(narc_tcp_client)); 52 | 53 | client->state = NARC_TCP_INITIALIZED; 54 | client->socket = NULL; 55 | client->stream = NULL; 56 | client->attempts = 0; 57 | 58 | return client; 59 | } 60 | 61 | int 62 | tcp_client_established(narc_tcp_client *client) 63 | { 64 | return (client->state == NARC_TCP_ESTABLISHED); 65 | } 66 | 67 | /*=============================== Callbacks ================================= */ 68 | 69 | void 70 | handle_tcp_connect(uv_connect_t* connection, int status) 71 | { 72 | narc_tcp_client *client = server.client; 73 | 74 | if (status == -1) { 75 | uv_close((uv_handle_t *)client->socket, (uv_close_cb)free); 76 | client->socket = NULL; 77 | narc_log(NARC_WARNING, "Error connecting to %s:%d (%d/%d)", 78 | server.host, 79 | server.port, 80 | client->attempts, 81 | server.max_connect_attempts); 82 | 83 | if (client->attempts == server.max_connect_attempts) { 84 | narc_log(NARC_WARNING, "Reached max connect attempts: %s:%d", 85 | server.host, 86 | server.port); 87 | exit(1); 88 | } else 89 | start_tcp_connect_timer(); 90 | 91 | } else { 92 | narc_log(NARC_NOTICE, "Connection established: %s:%d", server.host, server.port); 93 | 94 | client->stream = (uv_stream_t *)connection->handle; 95 | client->state = NARC_TCP_ESTABLISHED; 96 | client->attempts = 0; 97 | 98 | start_tcp_read(client->stream); 99 | } 100 | free(connection); 101 | } 102 | 103 | void 104 | handle_tcp_write(uv_write_t* req, int status) 105 | { 106 | free_tcp_write_req(req); 107 | } 108 | 109 | void 110 | handle_tcp_read_alloc_buffer(uv_handle_t *handle, size_t len, struct uv_buf_t *buf) 111 | { 112 | buf->base = malloc(len); 113 | buf->len = len; 114 | } 115 | 116 | // uv_buf_t 117 | // handle_tcp_read_alloc_buffer(uv_handle_t* handle, size_t size) 118 | // { 119 | // return uv_buf_init(malloc(size), size); 120 | // } 121 | 122 | void start_tcp_resolve(void); 123 | 124 | void 125 | handle_tcp_read(uv_stream_t* tcp, ssize_t nread, const struct uv_buf_t *buf) 126 | { 127 | if (nread >= 0) 128 | narc_log(NARC_WARNING, "server responded unexpectedly: %s", buf->base); 129 | 130 | else { 131 | narc_log(NARC_WARNING, "Connection dropped: %s:%d, attempting to re-connect", 132 | server.host, 133 | server.port); 134 | 135 | narc_tcp_client *client = (narc_tcp_client *)server.client; 136 | uv_close((uv_handle_t *)client->socket, (uv_close_cb)free); 137 | client->socket = NULL; 138 | client->state = NARC_TCP_INITIALIZED; 139 | 140 | start_tcp_connect_timer(); 141 | } 142 | if (buf->base) 143 | free(buf->base); 144 | } 145 | 146 | void 147 | handle_tcp_connect_timeout(uv_timer_t* timer) 148 | { 149 | start_tcp_resolve(); 150 | uv_close((uv_handle_t *)timer, (uv_close_cb)free); 151 | } 152 | 153 | void 154 | handle_tcp_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) 155 | { 156 | if (status >= 0){ 157 | narc_log(NARC_WARNING, "server resolved: %s", server.host); 158 | start_tcp_connect(res); 159 | }else{ 160 | narc_log(NARC_WARNING, "server did not resolve: %s", server.host); 161 | } 162 | } 163 | 164 | /*=============================== Watchers ================================== */ 165 | 166 | void 167 | start_tcp_resolve(void) 168 | { 169 | struct addrinfo hints; 170 | hints.ai_family = PF_INET; 171 | hints.ai_socktype = SOCK_STREAM; 172 | hints.ai_protocol = IPPROTO_TCP; 173 | hints.ai_flags = 0; 174 | narc_log(NARC_WARNING, "server resolving: %s", server.host); 175 | narc_tcp_client *client = (narc_tcp_client *)server.client; 176 | uv_getaddrinfo(server.loop, &client->resolver, handle_tcp_resolved, server.host, "80", &hints); 177 | } 178 | 179 | void 180 | start_tcp_connect(struct addrinfo *res) 181 | { 182 | narc_tcp_client *client = (narc_tcp_client *)server.client; 183 | uv_tcp_t *socket = (uv_tcp_t *)malloc(sizeof(uv_tcp_t)); 184 | 185 | uv_tcp_init(server.loop, socket); 186 | uv_tcp_keepalive(socket, 1, 60); 187 | 188 | struct sockaddr_in dest; 189 | uv_ip4_addr(res->ai_addr->sa_data, server.port, &dest); 190 | 191 | uv_connect_t *connect = malloc(sizeof(uv_connect_t)); 192 | if(uv_tcp_connect(connect, socket, (struct sockaddr *)&dest, handle_tcp_connect) == 0) { 193 | client->socket = socket; 194 | client->attempts += 1; 195 | } 196 | uv_freeaddrinfo(res); 197 | } 198 | 199 | void 200 | start_tcp_read(uv_stream_t *stream) 201 | { 202 | uv_read_start(stream, handle_tcp_read_alloc_buffer, handle_tcp_read); 203 | } 204 | 205 | void 206 | start_tcp_connect_timer(void) 207 | { 208 | uv_timer_t *timer = malloc(sizeof(uv_timer_t)); 209 | if (uv_timer_init(server.loop, timer) == 0) 210 | uv_timer_start(timer, handle_tcp_connect_timeout, server.connect_retry_delay, 0); 211 | } 212 | 213 | /*================================== API ==================================== */ 214 | 215 | void 216 | init_tcp_client(void) 217 | { 218 | server.client = (void *)new_tcp_client(); 219 | 220 | start_tcp_resolve(); 221 | } 222 | 223 | void 224 | clean_tcp_client(void) 225 | { 226 | narc_tcp_client *client = (narc_tcp_client *)server.client; 227 | if (client->socket != NULL) { 228 | uv_close((uv_handle_t *)client->socket, (uv_close_cb)free); 229 | client->socket = NULL; 230 | } 231 | // server.client = NULL; 232 | // free(client); 233 | } 234 | 235 | void 236 | submit_tcp_message(char *message) 237 | { 238 | narc_tcp_client *client = (narc_tcp_client *)server.client; 239 | 240 | if ( ! tcp_client_established(client) ) { 241 | sdsfree(message); 242 | return; 243 | } 244 | 245 | uv_write_t *req = (uv_write_t *)malloc(sizeof(uv_write_t)); 246 | uv_buf_t buf = uv_buf_init(message, strlen(message)); 247 | 248 | if (uv_write(req, client->stream, &buf, 1, handle_tcp_write) == 0) 249 | req->data = (void *)message; 250 | 251 | } 252 | -------------------------------------------------------------------------------- /src/sha1.c: -------------------------------------------------------------------------------- 1 | 2 | /* from valgrind tests */ 3 | 4 | /* ================ sha1.c ================ */ 5 | /* 6 | SHA-1 in C 7 | By Steve Reid 8 | 100% Public Domain 9 | 10 | Test Vectors (from FIPS PUB 180-1) 11 | "abc" 12 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 13 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 14 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 15 | A million repetitions of "a" 16 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 17 | */ 18 | 19 | /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ 20 | /* #define SHA1HANDSOFF * Copies data before messing with it. */ 21 | 22 | #define SHA1HANDSOFF 23 | 24 | #include 25 | #include 26 | #include /* for u_int*_t */ 27 | #if defined(__sun) 28 | #include "solarisfixes.h" 29 | #endif 30 | #include "sha1.h" 31 | #include "config.h" 32 | 33 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 34 | 35 | /* blk0() and blk() perform the initial expand. */ 36 | /* I got the idea of expanding during the round function from SSLeay */ 37 | #if BYTE_ORDER == LITTLE_ENDIAN 38 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 39 | |(rol(block->l[i],8)&0x00FF00FF)) 40 | #elif BYTE_ORDER == BIG_ENDIAN 41 | #define blk0(i) block->l[i] 42 | #else 43 | #error "Endianness not defined!" 44 | #endif 45 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 46 | ^block->l[(i+2)&15]^block->l[i&15],1)) 47 | 48 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 49 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 50 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 51 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 52 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 53 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 54 | 55 | 56 | /* Hash a single 512-bit block. This is the core of the algorithm. */ 57 | 58 | void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) 59 | { 60 | u_int32_t a, b, c, d, e; 61 | typedef union { 62 | unsigned char c[64]; 63 | u_int32_t l[16]; 64 | } CHAR64LONG16; 65 | #ifdef SHA1HANDSOFF 66 | CHAR64LONG16 block[1]; /* use array to appear as a pointer */ 67 | memcpy(block, buffer, 64); 68 | #else 69 | /* The following had better never be used because it causes the 70 | * pointer-to-const buffer to be cast into a pointer to non-const. 71 | * And the result is written through. I threw a "const" in, hoping 72 | * this will cause a diagnostic. 73 | */ 74 | CHAR64LONG16* block = (const CHAR64LONG16*)buffer; 75 | #endif 76 | /* Copy context->state[] to working vars */ 77 | a = state[0]; 78 | b = state[1]; 79 | c = state[2]; 80 | d = state[3]; 81 | e = state[4]; 82 | /* 4 rounds of 20 operations each. Loop unrolled. */ 83 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 84 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 85 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 86 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 87 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 88 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 89 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 90 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 91 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 92 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 93 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 94 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 95 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 96 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 97 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 98 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 99 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 100 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 101 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 102 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 103 | /* Add the working vars back into context.state[] */ 104 | state[0] += a; 105 | state[1] += b; 106 | state[2] += c; 107 | state[3] += d; 108 | state[4] += e; 109 | /* Wipe variables */ 110 | a = b = c = d = e = 0; 111 | #ifdef SHA1HANDSOFF 112 | memset(block, '\0', sizeof(block)); 113 | #endif 114 | } 115 | 116 | 117 | /* SHA1Init - Initialize new context */ 118 | 119 | void SHA1Init(SHA1_CTX* context) 120 | { 121 | /* SHA1 initialization constants */ 122 | context->state[0] = 0x67452301; 123 | context->state[1] = 0xEFCDAB89; 124 | context->state[2] = 0x98BADCFE; 125 | context->state[3] = 0x10325476; 126 | context->state[4] = 0xC3D2E1F0; 127 | context->count[0] = context->count[1] = 0; 128 | } 129 | 130 | 131 | /* Run your data through this. */ 132 | 133 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len) 134 | { 135 | u_int32_t i, j; 136 | 137 | j = context->count[0]; 138 | if ((context->count[0] += len << 3) < j) 139 | context->count[1]++; 140 | context->count[1] += (len>>29); 141 | j = (j >> 3) & 63; 142 | if ((j + len) > 63) { 143 | memcpy(&context->buffer[j], data, (i = 64-j)); 144 | SHA1Transform(context->state, context->buffer); 145 | for ( ; i + 63 < len; i += 64) { 146 | SHA1Transform(context->state, &data[i]); 147 | } 148 | j = 0; 149 | } 150 | else i = 0; 151 | memcpy(&context->buffer[j], &data[i], len - i); 152 | } 153 | 154 | 155 | /* Add padding and return the message digest. */ 156 | 157 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 158 | { 159 | unsigned i; 160 | unsigned char finalcount[8]; 161 | unsigned char c; 162 | 163 | #if 0 /* untested "improvement" by DHR */ 164 | /* Convert context->count to a sequence of bytes 165 | * in finalcount. Second element first, but 166 | * big-endian order within element. 167 | * But we do it all backwards. 168 | */ 169 | unsigned char *fcp = &finalcount[8]; 170 | 171 | for (i = 0; i < 2; i++) 172 | { 173 | u_int32_t t = context->count[i]; 174 | int j; 175 | 176 | for (j = 0; j < 4; t >>= 8, j++) 177 | *--fcp = (unsigned char) t; 178 | } 179 | #else 180 | for (i = 0; i < 8; i++) { 181 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 182 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ 183 | } 184 | #endif 185 | c = 0200; 186 | SHA1Update(context, &c, 1); 187 | while ((context->count[0] & 504) != 448) { 188 | c = 0000; 189 | SHA1Update(context, &c, 1); 190 | } 191 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 192 | for (i = 0; i < 20; i++) { 193 | digest[i] = (unsigned char) 194 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 195 | } 196 | /* Wipe variables */ 197 | memset(context, '\0', sizeof(*context)); 198 | memset(&finalcount, '\0', sizeof(finalcount)); 199 | } 200 | /* ================ end of sha1.c ================ */ 201 | 202 | #if 0 203 | #define BUFSIZE 4096 204 | 205 | int 206 | main(int argc, char **argv) 207 | { 208 | SHA1_CTX ctx; 209 | unsigned char hash[20], buf[BUFSIZE]; 210 | int i; 211 | 212 | for(i=0;i /* standard buffered input/output */ 35 | #include /* standard library definitions */ 36 | #include /* system error numbers */ 37 | #include /* definitions for system error logging */ 38 | #include /* string operations */ 39 | 40 | /*----------------------------------------------------------------------------- 41 | * Data types 42 | *----------------------------------------------------------------------------*/ 43 | 44 | static struct { 45 | const char *name; 46 | const int value; 47 | } validSyslogFacilities[] = { 48 | {"user", LOG_USER}, 49 | {"local0", LOG_LOCAL0}, 50 | {"local1", LOG_LOCAL1}, 51 | {"local2", LOG_LOCAL2}, 52 | {"local3", LOG_LOCAL3}, 53 | {"local4", LOG_LOCAL4}, 54 | {"local5", LOG_LOCAL5}, 55 | {"local6", LOG_LOCAL6}, 56 | {"local7", LOG_LOCAL7}, 57 | {NULL, 0} 58 | }; 59 | 60 | static struct { 61 | const char *name; 62 | const int value; 63 | } validSyslogPriorities[] = { 64 | {"emergency", LOG_EMERG}, 65 | {"alert", LOG_ALERT}, 66 | {"critical", LOG_CRIT}, 67 | {"error", LOG_ERR}, 68 | {"warning", LOG_WARNING}, 69 | {"notice", LOG_NOTICE}, 70 | {"info", LOG_INFO}, 71 | {"debug", LOG_DEBUG}, 72 | {NULL, 0} 73 | }; 74 | 75 | /*----------------------------------------------------------------------------- 76 | * Config file parsing 77 | *----------------------------------------------------------------------------*/ 78 | 79 | int 80 | yesnotoi(char *s) 81 | { 82 | if (!strcasecmp(s,"yes")) return 1; 83 | else if (!strcasecmp(s,"no")) return 0; 84 | else return -1; 85 | } 86 | 87 | void 88 | load_server_config_from_string(char *config) 89 | { 90 | char *err = NULL; 91 | int linenum = 0, totlines, i; 92 | sds *lines; 93 | 94 | lines = sdssplitlen(config,strlen(config),"\n",1,&totlines); 95 | 96 | for (i = 0; i < totlines; i++) { 97 | sds *argv; 98 | int argc; 99 | 100 | linenum = i+1; 101 | lines[i] = sdstrim(lines[i]," \t\r\n"); 102 | 103 | /* Skip comments and blank lines */ 104 | if (lines[i][0] == '#' || lines[i][0] == '\0') continue; 105 | 106 | /* Split into arguments */ 107 | argv = sdssplitargs(lines[i],&argc); 108 | if (argv == NULL) { 109 | err = "Unbalanced quotes in configuration line"; 110 | goto loaderr; 111 | } 112 | 113 | /* Skip this line if the resulting command vector is empty. */ 114 | if (argc == 0) { 115 | sdsfreesplitres(argv,argc); 116 | continue; 117 | } 118 | sdstolower(argv[0]); 119 | 120 | /* Execute config directives */ 121 | if (!strcasecmp(argv[0], "daemonize") && argc == 2) { 122 | if ((server.daemonize = yesnotoi(argv[1])) == -1) { 123 | err = "argument must be 'yes' or 'no'"; goto loaderr; 124 | } 125 | } else if (!strcasecmp(argv[0], "pidfile") && argc == 2) { 126 | free(server.pidfile); 127 | server.pidfile = strdup(argv[1]); 128 | } else if (!strcasecmp(argv[0], "loglevel") && argc == 2) { 129 | if (!strcasecmp(argv[1],"debug")) server.verbosity = NARC_DEBUG; 130 | else if (!strcasecmp(argv[1],"verbose")) server.verbosity = NARC_VERBOSE; 131 | else if (!strcasecmp(argv[1],"notice")) server.verbosity = NARC_NOTICE; 132 | else if (!strcasecmp(argv[1],"warning")) server.verbosity = NARC_WARNING; 133 | else { 134 | err = "Invalid log level. Must be one of debug, notice, warning"; 135 | goto loaderr; 136 | } 137 | } else if (!strcasecmp(argv[0],"logfile") && argc == 2) { 138 | FILE *logfp; 139 | 140 | free(server.logfile); 141 | server.logfile = strdup(argv[1]); 142 | if (server.logfile[0] != '\0') { 143 | /* Test if we are able to open the file. The server will not 144 | * be able to abort just for this problem later... */ 145 | logfp = fopen(server.logfile,"a"); 146 | if (logfp == NULL) { 147 | err = sdscatprintf(sdsempty(), 148 | "Can't open the log file: %s", strerror(errno)); 149 | goto loaderr; 150 | } 151 | fclose(logfp); 152 | } 153 | } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) { 154 | if ((server.syslog_enabled = yesnotoi(argv[1])) == -1) { 155 | err = "argument must be 'yes' or 'no'"; goto loaderr; 156 | } 157 | } else if (!strcasecmp(argv[0],"syslog-ident") && argc == 2) { 158 | if (server.syslog_ident) free(server.syslog_ident); 159 | server.syslog_ident = strdup(argv[1]); 160 | } else if (!strcasecmp(argv[0],"syslog-facility") && argc == 2) { 161 | int i; 162 | 163 | for (i = 0; validSyslogFacilities[i].name; i++) { 164 | if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) { 165 | server.syslog_facility = validSyslogFacilities[i].value; 166 | break; 167 | } 168 | } 169 | 170 | if (!validSyslogFacilities[i].name) { 171 | err = "Invalid log facility. Must be one of 'user' or between 'local0-local7'"; 172 | goto loaderr; 173 | } 174 | } else if (!strcasecmp(argv[0], "remote-host") && argc == 2) { 175 | free(server.host); 176 | server.host = strdup(argv[1]); 177 | } else if (!strcasecmp(argv[0], "remote-port") && argc == 2) { 178 | server.port = atoi(argv[1]); 179 | if (server.port < 0 || server.port > 65535) { 180 | err = "Invalid port"; goto loaderr; 181 | } 182 | } else if (!strcasecmp(argv[0], "remote-proto") && argc == 2) { 183 | if (!strcasecmp(argv[1],"udp")) server.protocol = NARC_PROTO_UDP; 184 | else if (!strcasecmp(argv[1],"tcp")) server.protocol = NARC_PROTO_TCP; 185 | else { 186 | err = "Invalid protocol. Must be either udp or tcp"; 187 | goto loaderr; 188 | } 189 | } else if (!strcasecmp(argv[0], "max-connect-attempts") && argc == 2) { 190 | server.max_connect_attempts = atoi(argv[1]); 191 | } else if (!strcasecmp(argv[0], "connect-retry-delay") && argc == 2) { 192 | server.connect_retry_delay = atoll(argv[1]); 193 | } else if (!strcasecmp(argv[0], "max-open-attempts") && argc == 2) { 194 | server.max_open_attempts = atoi(argv[1]); 195 | } else if (!strcasecmp(argv[0], "open-retry-delay") && argc == 2) { 196 | server.open_retry_delay = atoll(argv[1]); 197 | } else if (!strcasecmp(argv[0], "stream-id") && argc == 2) { 198 | free(server.stream_id); 199 | server.stream_id = strdup(argv[1]); 200 | } else if (!strcasecmp(argv[0], "stream-facility") && argc == 2) { 201 | int i; 202 | 203 | for (i = 0; validSyslogFacilities[i].name; i++) { 204 | if (!strcasecmp(validSyslogFacilities[i].name, argv[1])) { 205 | server.stream_facility = validSyslogFacilities[i].value; 206 | break; 207 | } 208 | } 209 | 210 | if (!validSyslogFacilities[i].name) { 211 | err = "Invalid stream facility. Must be one of 'user' or between 'local0-local7'"; 212 | goto loaderr; 213 | } 214 | } else if (!strcasecmp(argv[0], "stream-priority") && argc == 2) { 215 | int i; 216 | 217 | for (i = 0; validSyslogPriorities[i].name; i++) { 218 | if (!strcasecmp(validSyslogPriorities[i].name, argv[1])) { 219 | server.stream_priority = validSyslogPriorities[i].value; 220 | break; 221 | } 222 | } 223 | 224 | if (!validSyslogPriorities[i].name) { 225 | err = "Invalid stream priority. Must be one of: 'emergency', 'alert', 'critical', 'error', 'warning', 'info', 'notice', or 'debug'"; 226 | goto loaderr; 227 | } 228 | } else if (!strcasecmp(argv[0],"stream") && argc == 3) { 229 | char *id = sdsdup(argv[1]); 230 | char *file = sdsdup(argv[2]); 231 | narc_stream *stream = new_stream(id, file); 232 | listAddNodeTail(server.streams, (void *)stream); 233 | } else if (!strcasecmp(argv[0],"rate-limit") && argc == 2) { 234 | server.rate_limit = atoi(argv[1]); 235 | } else if (!strcasecmp(argv[0],"rate-time") && argc == 2) { 236 | server.rate_time = atoi(argv[1]); 237 | } else if (!strcasecmp(argv[0],"truncate-limit") && argc == 2) { 238 | server.truncate_limit = atoi(argv[1]); 239 | } else { 240 | err = "Bad directive or wrong number of arguments"; goto loaderr; 241 | } 242 | sdsfreesplitres(argv,argc); 243 | } 244 | sdsfreesplitres(lines,totlines); 245 | 246 | return; 247 | 248 | loaderr: 249 | fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); 250 | fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); 251 | fprintf(stderr, ">>> '%s'\n", lines[i]); 252 | fprintf(stderr, "%s\n", err); 253 | exit(1); 254 | } 255 | 256 | /* Load the server configuration from the specified filename. 257 | * The function appends the additional configuration directives stored 258 | * in the 'options' string to the config file before loading. 259 | * 260 | * Both filename and options can be NULL, in such a case are considered 261 | * empty. This way load_server_config can be used to just load a file or 262 | * just load a string. */ 263 | void 264 | load_server_config(char *filename, char *options) 265 | { 266 | sds config = sdsempty(); 267 | char buf[NARC_CONFIGLINE_MAX+1]; 268 | 269 | /* Load the file content */ 270 | if (filename) { 271 | FILE *fp; 272 | 273 | if (filename[0] == '-' && filename[1] == '\0') { 274 | fp = stdin; 275 | } else { 276 | if ((fp = fopen(filename,"r")) == NULL) { 277 | narc_log(NARC_WARNING, 278 | "Fatal error, can't open config file '%s'", filename); 279 | exit(1); 280 | } 281 | } 282 | while(fgets(buf,NARC_CONFIGLINE_MAX+1,fp) != NULL) 283 | config = sdscat(config,buf); 284 | if (fp != stdin) fclose(fp); 285 | } 286 | /* Append the additional options */ 287 | if (options) { 288 | config = sdscat(config,"\n"); 289 | config = sdscat(config,options); 290 | } 291 | load_server_config_from_string(config); 292 | sdsfree(config); 293 | } 294 | -------------------------------------------------------------------------------- /src/crc64.c: -------------------------------------------------------------------------------- 1 | /* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0. 2 | * 3 | * Specification of this CRC64 variant follows: 4 | * Name: crc-64-jones 5 | * Width: 64 bites 6 | * Poly: 0xad93d23594c935a9 7 | * Reflected In: True 8 | * Xor_In: 0xffffffffffffffff 9 | * Reflected_Out: True 10 | * Xor_Out: 0x0 11 | * Check("123456789"): 0xe9c6d914c4b8d9ca 12 | * 13 | * Copyright (c) 2012, Salvatore Sanfilippo 14 | * All rights reserved. 15 | * 16 | * Redistribution and use in source and binary forms, with or without 17 | * modification, are permitted provided that the following conditions are met: 18 | * 19 | * * Redistributions of source code must retain the above copyright notice, 20 | * this list of conditions and the following disclaimer. 21 | * * Redistributions in binary form must reproduce the above copyright 22 | * notice, this list of conditions and the following disclaimer in the 23 | * documentation and/or other materials provided with the distribution. 24 | * * Neither the name of Redis nor the names of its contributors may be used 25 | * to endorse or promote products derived from this software without 26 | * specific prior written permission. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 32 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | * POSSIBILITY OF SUCH DAMAGE. */ 39 | 40 | #include 41 | 42 | static const uint64_t crc64_tab[256] = { 43 | UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979), 44 | UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b), 45 | UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6), 46 | UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04), 47 | UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c), 48 | UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe), 49 | UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183), 50 | UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371), 51 | UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8), 52 | UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a), 53 | UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077), 54 | UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285), 55 | UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d), 56 | UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f), 57 | UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02), 58 | UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0), 59 | UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b), 60 | UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489), 61 | UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4), 62 | UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206), 63 | UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e), 64 | UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc), 65 | UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81), 66 | UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73), 67 | UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa), 68 | UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08), 69 | UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75), 70 | UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87), 71 | UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f), 72 | UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d), 73 | UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100), 74 | UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2), 75 | UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416), 76 | UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4), 77 | UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299), 78 | UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b), 79 | UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63), 80 | UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891), 81 | UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec), 82 | UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e), 83 | UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97), 84 | UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965), 85 | UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18), 86 | UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea), 87 | UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2), 88 | UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710), 89 | UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d), 90 | UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f), 91 | UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14), 92 | UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6), 93 | UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b), 94 | UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69), 95 | UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561), 96 | UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793), 97 | UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee), 98 | UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c), 99 | UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495), 100 | UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667), 101 | UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a), 102 | UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8), 103 | UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0), 104 | UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812), 105 | UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f), 106 | UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d), 107 | UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc), 108 | UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e), 109 | UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643), 110 | UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1), 111 | UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9), 112 | UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b), 113 | UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836), 114 | UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4), 115 | UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d), 116 | UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf), 117 | UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2), 118 | UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30), 119 | UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138), 120 | UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca), 121 | UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7), 122 | UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545), 123 | UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce), 124 | UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c), 125 | UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941), 126 | UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3), 127 | UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb), 128 | UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349), 129 | UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734), 130 | UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6), 131 | UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f), 132 | UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd), 133 | UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0), 134 | UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432), 135 | UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a), 136 | UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8), 137 | UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5), 138 | UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47), 139 | UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3), 140 | UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51), 141 | UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c), 142 | UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de), 143 | UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6), 144 | UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124), 145 | UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559), 146 | UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab), 147 | UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222), 148 | UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0), 149 | UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad), 150 | UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f), 151 | UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57), 152 | UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5), 153 | UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8), 154 | UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a), 155 | UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1), 156 | UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053), 157 | UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e), 158 | UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc), 159 | UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4), 160 | UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26), 161 | UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b), 162 | UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9), 163 | UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20), 164 | UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2), 165 | UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf), 166 | UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d), 167 | UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355), 168 | UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7), 169 | UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da), 170 | UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728), 171 | }; 172 | 173 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) { 174 | uint64_t j; 175 | 176 | for (j = 0; j < l; j++) { 177 | uint8_t byte = s[j]; 178 | crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8); 179 | } 180 | return crc; 181 | } 182 | 183 | /* Test main */ 184 | #ifdef TEST_MAIN 185 | #include 186 | int main(void) { 187 | printf("e9c6d914c4b8d9ca == %016llx\n", 188 | (unsigned long long) crc64(0,(unsigned char*)"123456789",9)); 189 | return 0; 190 | } 191 | #endif 192 | -------------------------------------------------------------------------------- /src/adlist.c: -------------------------------------------------------------------------------- 1 | /* adlist.c - A generic doubly linked list implementation 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 | 32 | #include 33 | #include "adlist.h" 34 | // #include "malloc.h" 35 | 36 | /* Create a new list. The created list can be freed with 37 | * AlFreeList(), but private value of every node need to be freed 38 | * by the user before to call AlFreeList(). 39 | * 40 | * On error, NULL is returned. Otherwise the pointer to the new list. */ 41 | list *listCreate(void) 42 | { 43 | struct list *list; 44 | 45 | if ((list = malloc(sizeof(*list))) == NULL) 46 | return NULL; 47 | list->head = list->tail = NULL; 48 | list->len = 0; 49 | list->dup = NULL; 50 | list->free = NULL; 51 | list->match = NULL; 52 | return list; 53 | } 54 | 55 | /* Free the whole list. 56 | * 57 | * This function can't fail. */ 58 | void listRelease(list *list) 59 | { 60 | unsigned long len; 61 | listNode *current, *next; 62 | 63 | current = list->head; 64 | len = list->len; 65 | while(len--) { 66 | next = current->next; 67 | if (list->free) list->free(current->value); 68 | free(current); 69 | current = next; 70 | } 71 | free(list); 72 | } 73 | 74 | /* Add a new node to the list, to head, contaning the specified 'value' 75 | * pointer as value. 76 | * 77 | * On error, NULL is returned and no operation is performed (i.e. the 78 | * list remains unaltered). 79 | * On success the 'list' pointer you pass to the function is returned. */ 80 | list *listAddNodeHead(list *list, void *value) 81 | { 82 | listNode *node; 83 | 84 | if ((node = malloc(sizeof(*node))) == NULL) 85 | return NULL; 86 | node->value = value; 87 | if (list->len == 0) { 88 | list->head = list->tail = node; 89 | node->prev = node->next = NULL; 90 | } else { 91 | node->prev = NULL; 92 | node->next = list->head; 93 | list->head->prev = node; 94 | list->head = node; 95 | } 96 | list->len++; 97 | return list; 98 | } 99 | 100 | /* Add a new node to the list, to tail, containing the specified 'value' 101 | * pointer as value. 102 | * 103 | * On error, NULL is returned and no operation is performed (i.e. the 104 | * list remains unaltered). 105 | * On success the 'list' pointer you pass to the function is returned. */ 106 | list *listAddNodeTail(list *list, void *value) 107 | { 108 | listNode *node; 109 | 110 | if ((node = malloc(sizeof(*node))) == NULL) 111 | return NULL; 112 | node->value = value; 113 | if (list->len == 0) { 114 | list->head = list->tail = node; 115 | node->prev = node->next = NULL; 116 | } else { 117 | node->prev = list->tail; 118 | node->next = NULL; 119 | list->tail->next = node; 120 | list->tail = node; 121 | } 122 | list->len++; 123 | return list; 124 | } 125 | 126 | list *listInsertNode(list *list, listNode *old_node, void *value, int after) { 127 | listNode *node; 128 | 129 | if ((node = malloc(sizeof(*node))) == NULL) 130 | return NULL; 131 | node->value = value; 132 | if (after) { 133 | node->prev = old_node; 134 | node->next = old_node->next; 135 | if (list->tail == old_node) { 136 | list->tail = node; 137 | } 138 | } else { 139 | node->next = old_node; 140 | node->prev = old_node->prev; 141 | if (list->head == old_node) { 142 | list->head = node; 143 | } 144 | } 145 | if (node->prev != NULL) { 146 | node->prev->next = node; 147 | } 148 | if (node->next != NULL) { 149 | node->next->prev = node; 150 | } 151 | list->len++; 152 | return list; 153 | } 154 | 155 | /* Remove the specified node from the specified list. 156 | * It's up to the caller to free the private value of the node. 157 | * 158 | * This function can't fail. */ 159 | void listDelNode(list *list, listNode *node) 160 | { 161 | if (node->prev) 162 | node->prev->next = node->next; 163 | else 164 | list->head = node->next; 165 | if (node->next) 166 | node->next->prev = node->prev; 167 | else 168 | list->tail = node->prev; 169 | if (list->free) list->free(node->value); 170 | free(node); 171 | list->len--; 172 | } 173 | 174 | /* Returns a list iterator 'iter'. After the initialization every 175 | * call to listNext() will return the next element of the list. 176 | * 177 | * This function can't fail. */ 178 | listIter *listGetIterator(list *list, int direction) 179 | { 180 | listIter *iter; 181 | 182 | if ((iter = malloc(sizeof(*iter))) == NULL) return NULL; 183 | if (direction == AL_START_HEAD) 184 | iter->next = list->head; 185 | else 186 | iter->next = list->tail; 187 | iter->direction = direction; 188 | return iter; 189 | } 190 | 191 | /* Release the iterator memory */ 192 | void listReleaseIterator(listIter *iter) { 193 | free(iter); 194 | } 195 | 196 | /* Create an iterator in the list private iterator structure */ 197 | void listRewind(list *list, listIter *li) { 198 | li->next = list->head; 199 | li->direction = AL_START_HEAD; 200 | } 201 | 202 | void listRewindTail(list *list, listIter *li) { 203 | li->next = list->tail; 204 | li->direction = AL_START_TAIL; 205 | } 206 | 207 | /* Return the next element of an iterator. 208 | * It's valid to remove the currently returned element using 209 | * listDelNode(), but not to remove other elements. 210 | * 211 | * The function returns a pointer to the next element of the list, 212 | * or NULL if there are no more elements, so the classical usage patter 213 | * is: 214 | * 215 | * iter = listGetIterator(list,); 216 | * while ((node = listNext(iter)) != NULL) { 217 | * doSomethingWith(listNodeValue(node)); 218 | * } 219 | * 220 | * */ 221 | listNode *listNext(listIter *iter) 222 | { 223 | listNode *current = iter->next; 224 | 225 | if (current != NULL) { 226 | if (iter->direction == AL_START_HEAD) 227 | iter->next = current->next; 228 | else 229 | iter->next = current->prev; 230 | } 231 | return current; 232 | } 233 | 234 | /* Duplicate the whole list. On out of memory NULL is returned. 235 | * On success a copy of the original list is returned. 236 | * 237 | * The 'Dup' method set with listSetDupMethod() function is used 238 | * to copy the node value. Otherwise the same pointer value of 239 | * the original node is used as value of the copied node. 240 | * 241 | * The original list both on success or error is never modified. */ 242 | list *listDup(list *orig) 243 | { 244 | list *copy; 245 | listIter *iter; 246 | listNode *node; 247 | 248 | if ((copy = listCreate()) == NULL) 249 | return NULL; 250 | copy->dup = orig->dup; 251 | copy->free = orig->free; 252 | copy->match = orig->match; 253 | iter = listGetIterator(orig, AL_START_HEAD); 254 | while((node = listNext(iter)) != NULL) { 255 | void *value; 256 | 257 | if (copy->dup) { 258 | value = copy->dup(node->value); 259 | if (value == NULL) { 260 | listRelease(copy); 261 | listReleaseIterator(iter); 262 | return NULL; 263 | } 264 | } else 265 | value = node->value; 266 | if (listAddNodeTail(copy, value) == NULL) { 267 | listRelease(copy); 268 | listReleaseIterator(iter); 269 | return NULL; 270 | } 271 | } 272 | listReleaseIterator(iter); 273 | return copy; 274 | } 275 | 276 | /* Search the list for a node matching a given key. 277 | * The match is performed using the 'match' method 278 | * set with listSetMatchMethod(). If no 'match' method 279 | * is set, the 'value' pointer of every node is directly 280 | * compared with the 'key' pointer. 281 | * 282 | * On success the first matching node pointer is returned 283 | * (search starts from head). If no matching node exists 284 | * NULL is returned. */ 285 | listNode *listSearchKey(list *list, void *key) 286 | { 287 | listIter *iter; 288 | listNode *node; 289 | 290 | iter = listGetIterator(list, AL_START_HEAD); 291 | while((node = listNext(iter)) != NULL) { 292 | if (list->match) { 293 | if (list->match(node->value, key)) { 294 | listReleaseIterator(iter); 295 | return node; 296 | } 297 | } else { 298 | if (key == node->value) { 299 | listReleaseIterator(iter); 300 | return node; 301 | } 302 | } 303 | } 304 | listReleaseIterator(iter); 305 | return NULL; 306 | } 307 | 308 | /* Return the element at the specified zero-based index 309 | * where 0 is the head, 1 is the element next to head 310 | * and so on. Negative integers are used in order to count 311 | * from the tail, -1 is the last element, -2 the penultimate 312 | * and so on. If the index is out of range NULL is returned. */ 313 | listNode *listIndex(list *list, long index) { 314 | listNode *n; 315 | 316 | if (index < 0) { 317 | index = (-index)-1; 318 | n = list->tail; 319 | while(index-- && n) n = n->prev; 320 | } else { 321 | n = list->head; 322 | while(index-- && n) n = n->next; 323 | } 324 | return n; 325 | } 326 | 327 | /* Rotate the list removing the tail node and inserting it to the head. */ 328 | void listRotate(list *list) { 329 | listNode *tail = list->tail; 330 | 331 | if (listLength(list) <= 1) return; 332 | 333 | /* Detach current tail */ 334 | list->tail = tail->prev; 335 | list->tail->next = NULL; 336 | /* Move it as head */ 337 | list->head->prev = tail; 338 | tail->prev = NULL; 339 | tail->next = list->head; 340 | list->head = tail; 341 | } 342 | -------------------------------------------------------------------------------- /src/narc.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #include "narc.h" 27 | #include "stream.h" 28 | #include "config.h" 29 | #include "tcp_client.h" 30 | #include "udp_client.h" 31 | 32 | // #include "malloc.h" /* total memory usage aware version of malloc/free */ 33 | #include "sds.h" /* dynamic safe strings */ 34 | #include "util.h" /* Misc functions useful in many places */ 35 | 36 | #include /* standard buffered input/output */ 37 | #include /* standard library definitions */ 38 | #include /* definitions for system error logging */ 39 | #include /* time types */ 40 | #include /* standard symbolic constants and types */ 41 | #include /* set program locale */ 42 | #include /* string operations */ 43 | 44 | /*================================= Globals ================================= */ 45 | 46 | /* Global vars */ 47 | struct narc_server server; /* server global state */ 48 | 49 | /*============================ Utility functions ============================ */ 50 | 51 | /* Low level logging. To use only for very big messages, otherwise 52 | * narc_log() is to prefer. */ 53 | void 54 | narc_log_raw(int level, const char *msg) 55 | { 56 | const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; 57 | const char *c = ".-*#"; 58 | FILE *fp; 59 | char buf[64]; 60 | int rawmode = (level & NARC_LOG_RAW); 61 | int log_to_stdout = server.logfile[0] == '\0'; 62 | 63 | level &= 0xff; /* clear flags */ 64 | if (level < server.verbosity) return; 65 | 66 | fp = log_to_stdout ? stdout : fopen(server.logfile,"a"); 67 | if (!fp) return; 68 | 69 | if (rawmode) { 70 | fprintf(fp,"%s",msg); 71 | } else { 72 | int off; 73 | struct timeval tv; 74 | 75 | gettimeofday(&tv,NULL); 76 | off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec)); 77 | snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); 78 | fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg); 79 | } 80 | fflush(fp); 81 | 82 | if (!log_to_stdout) fclose(fp); 83 | if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); 84 | } 85 | 86 | /* Like narc_log_raw() but with printf-alike support. This is the function that 87 | * is used across the code. The raw version is only used in order to dump 88 | * the INFO output on crash. */ 89 | void 90 | narc_log(int level, const char *fmt, ...) 91 | { 92 | va_list ap; 93 | char msg[NARC_MAX_LOGMSG_LEN]; 94 | 95 | if ((level&0xff) < server.verbosity) return; 96 | 97 | va_start(ap, fmt); 98 | vsnprintf(msg, sizeof(msg), fmt, ap); 99 | va_end(ap); 100 | 101 | narc_log_raw(level,msg); 102 | } 103 | 104 | void 105 | handle_message(char *id, char *body) 106 | { 107 | char *message; 108 | message = sdscatprintf(sdsempty(), "<%d>%s %s %s %s\n", 109 | server.stream_facility + server.stream_priority, 110 | server.time, server.stream_id, id, body); 111 | 112 | switch (server.protocol) { 113 | case NARC_PROTO_UDP : 114 | submit_udp_message(message); 115 | break; 116 | case NARC_PROTO_TCP : 117 | submit_tcp_message(message); 118 | break; 119 | case NARC_PROTO_SYSLOG : 120 | narc_log(NARC_WARNING, "syslog is not yet implemented"); 121 | exit(1); 122 | break; 123 | } 124 | } 125 | 126 | void 127 | calculate_time(uv_timer_t* handle) 128 | { 129 | struct timeval tv; 130 | gettimeofday(&tv,NULL); 131 | strftime(server.time,sizeof(server.time),"%b %d %T",localtime(&tv.tv_sec)); 132 | } 133 | 134 | void 135 | start_timer_loop() 136 | { 137 | calculate_time(NULL); 138 | uv_timer_init(server.loop,&server.time_timer); 139 | uv_timer_start(&server.time_timer,calculate_time,500,500); 140 | } 141 | 142 | /*=========================== Server initialization ========================= */ 143 | 144 | void 145 | init_server_config(void) 146 | { 147 | server.pidfile = strdup(NARC_DEFAULT_PIDFILE); 148 | server.arch_bits = (sizeof(long) == 8) ? 64 : 32; 149 | server.host = strdup(NARC_DEFAULT_HOST); 150 | server.port = NARC_DEFAULT_PORT; 151 | server.protocol = NARC_DEFAULT_PROTO; 152 | server.stream_id = strdup(NARC_DEFAULT_STREAM_ID); 153 | server.stream_facility = NARC_DEFAULT_STREAM_FACILITY; 154 | server.stream_priority = NARC_DEFAULT_STREAM_PRIORITY; 155 | server.verbosity = NARC_DEFAULT_VERBOSITY; 156 | server.daemonize = NARC_DEFAULT_DAEMONIZE; 157 | server.logfile = strdup(NARC_DEFAULT_LOGFILE); 158 | server.syslog_enabled = NARC_DEFAULT_SYSLOG_ENABLED; 159 | server.syslog_ident = strdup(NARC_DEFAULT_SYSLOG_IDENT); 160 | server.syslog_facility = LOG_LOCAL0; 161 | server.max_open_attempts = NARC_DEFAULT_OPEN_ATTEMPTS; 162 | server.open_retry_delay = NARC_DEFAULT_OPEN_DELAY; 163 | server.max_connect_attempts = NARC_DEFAULT_CONNECT_ATTEMPTS; 164 | server.connect_retry_delay = NARC_DEFAULT_CONNECT_DELAY; 165 | server.rate_limit = NARC_DEFAULT_RATE_LIMIT; 166 | server.rate_time = NARC_DEFAULT_RATE_TIME; 167 | server.truncate_limit = NARC_DEFAULT_TRUNCATE_LIMIT; 168 | server.streams = listCreate(); 169 | listSetFreeMethod(server.streams, free_stream); 170 | } 171 | 172 | void 173 | clean_server_config(void) 174 | { 175 | free(server.pidfile); 176 | free(server.host); 177 | free(server.stream_id); 178 | free(server.logfile); 179 | free(server.syslog_ident); 180 | switch (server.protocol) { 181 | case NARC_PROTO_UDP : 182 | free((narc_udp_client *)server.client); 183 | break; 184 | case NARC_PROTO_TCP : 185 | free((narc_tcp_client *)server.client); 186 | break; 187 | } 188 | 189 | } 190 | 191 | void 192 | init_server(void) 193 | { 194 | if (server.syslog_enabled) 195 | openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT, server.syslog_facility); 196 | 197 | server.loop = uv_default_loop(); 198 | 199 | listIter *iter; 200 | listNode *node; 201 | 202 | iter = listGetIterator(server.streams, AL_START_HEAD); 203 | while ((node = listNext(iter)) != NULL) 204 | init_stream((narc_stream *)listNodeValue(node)); 205 | 206 | listReleaseIterator(iter); 207 | 208 | switch (server.protocol) { 209 | case NARC_PROTO_UDP : 210 | init_udp_client(); 211 | break; 212 | case NARC_PROTO_TCP : 213 | init_tcp_client(); 214 | break; 215 | case NARC_PROTO_SYSLOG : 216 | narc_log(NARC_WARNING, "syslog is not yet implemented"); 217 | exit(1); 218 | break; 219 | } 220 | } 221 | 222 | void 223 | clean_server(void) 224 | { 225 | switch (server.protocol) { 226 | case NARC_PROTO_UDP : 227 | clean_udp_client(); 228 | break; 229 | case NARC_PROTO_TCP : 230 | clean_tcp_client(); 231 | break; 232 | } 233 | } 234 | 235 | /* =================================== Main! ================================ */ 236 | 237 | void 238 | create_pid_file(void) 239 | { 240 | /* Try to write the pid file in a best-effort way. */ 241 | FILE *fp = fopen(server.pidfile,"w"); 242 | if (fp) { 243 | fprintf(fp,"%d\n",(int)getpid()); 244 | fclose(fp); 245 | } 246 | } 247 | 248 | void 249 | daemonize(void) 250 | { 251 | int fd; 252 | 253 | if (fork() != 0) exit(0); /* parent exits */ 254 | setsid(); /* create a new session */ 255 | 256 | /* Every output goes to /dev/null. If Narc is daemonized but 257 | * the 'logfile' is set to 'stdout' in the configuration file 258 | * it will not log at all. */ 259 | if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { 260 | dup2(fd, STDIN_FILENO); 261 | dup2(fd, STDOUT_FILENO); 262 | dup2(fd, STDERR_FILENO); 263 | if (fd > STDERR_FILENO) close(fd); 264 | } 265 | } 266 | 267 | void 268 | version(void) 269 | { 270 | printf("Narc v=%s bits=%d", 271 | NARC_VERSION, 272 | sizeof(long) == 4 ? 32 : 64); 273 | exit(0); 274 | } 275 | 276 | void 277 | usage(void) 278 | { 279 | fprintf(stderr,"Usage: ./narc [/path/to/narc.conf] [options]\n"); 280 | fprintf(stderr," ./narc - (read config from stdin)\n"); 281 | fprintf(stderr," ./narc -v or --version\n"); 282 | fprintf(stderr," ./narc -h or --help\n\n"); 283 | fprintf(stderr,"Examples:\n"); 284 | fprintf(stderr," ./narc (run the server with default conf)\n"); 285 | fprintf(stderr," ./narc /etc/narc.conf\n"); 286 | fprintf(stderr," ./narc --port 7777\n"); 287 | fprintf(stderr," ./narc /etc/mynarc.conf --loglevel verbose\n\n"); 288 | exit(1); 289 | } 290 | 291 | void 292 | narc_set_proc_title(char *title) 293 | { 294 | #ifdef USE_SETPROCTITLE 295 | setproctitle("%s", title); 296 | #else 297 | NARC_NOTUSED(title); 298 | #endif 299 | } 300 | 301 | void 302 | close_handles(uv_handle_t* handle, void* arg) { 303 | if (!(handle->flags & (0x01 | 0x02))){ 304 | if (handle->type == UV_SIGNAL || handle == &server.time_timer) { 305 | uv_close(handle, NULL); 306 | } else { 307 | uv_close(handle, (uv_close_cb)free); 308 | } 309 | } 310 | } 311 | 312 | void 313 | stop(void) 314 | { 315 | narc_log(NARC_NOTICE, "Stopping"); 316 | // uv_stop(server.loop); 317 | } 318 | 319 | void signal_handler(uv_signal_t *handle, int signum) { 320 | uv_signal_stop(handle); 321 | uv_close((uv_handle_t*)handle, NULL); 322 | uv_signal_stop(&server.loop->child_watcher); 323 | uv_close((uv_handle_t*)&server.loop->child_watcher, NULL); 324 | listRelease(server.streams); 325 | clean_server(); 326 | stop(); 327 | uv_walk(server.loop, close_handles, NULL); 328 | } 329 | 330 | int 331 | main(int argc, char **argv) 332 | { 333 | setlocale(LC_COLLATE,""); 334 | init_server_config(); 335 | 336 | if (argc >= 2) { 337 | int j = 1; /* First option to parse in argv[] */ 338 | sds options = sdsempty(); 339 | char *configfile = NULL; 340 | 341 | /* Handle special options --help and --version */ 342 | if (strcmp(argv[1], "-v") == 0 || 343 | strcmp(argv[1], "--version") == 0) version(); 344 | if (strcmp(argv[1], "--help") == 0 || 345 | strcmp(argv[1], "-h") == 0) usage(); 346 | 347 | /* First argument is the config file name? */ 348 | if (argv[j][0] != '-' || argv[j][1] != '-') 349 | configfile = argv[j++]; 350 | /* All the other options are parsed and conceptually appended to the 351 | * configuration file. For instance --port 6380 will generate the 352 | * string "port 6380\n" to be parsed after the actual file name 353 | * is parsed, if any. */ 354 | while(j != argc) { 355 | if (argv[j][0] == '-' && argv[j][1] == '-') { 356 | /* Option name */ 357 | if (sdslen(options)) options = sdscat(options,"\n"); 358 | options = sdscat(options,argv[j]+2); 359 | options = sdscat(options," "); 360 | } else { 361 | /* Option argument */ 362 | options = sdscatrepr(options,argv[j],strlen(argv[j])); 363 | options = sdscat(options," "); 364 | } 365 | j++; 366 | } 367 | load_server_config(configfile, options); 368 | sdsfree(options); 369 | } else { 370 | narc_log(NARC_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/narc.conf", argv[0]); 371 | } 372 | 373 | if (server.daemonize) daemonize(); 374 | init_server(); 375 | if (server.daemonize) create_pid_file(); 376 | narc_set_proc_title(argv[0]); 377 | 378 | start_timer_loop(); 379 | 380 | narc_log(NARC_WARNING, "Narc started, version " NARC_VERSION); 381 | narc_log(NARC_WARNING, "Waiting for events on %d files", (int)listLength(server.streams)); 382 | 383 | uv_signal_t quit_signal; 384 | uv_signal_init(server.loop, &quit_signal); 385 | uv_signal_start(&quit_signal, signal_handler, SIGTERM); 386 | 387 | uv_run(server.loop, UV_RUN_DEFAULT); 388 | clean_server_config(); 389 | // listRelease(server.streams); 390 | return uv_loop_close(server.loop); 391 | } 392 | -------------------------------------------------------------------------------- /src/stream.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | /* 3 | * CDDL HEADER START 4 | * 5 | * The contents of this file are subject to the terms of the 6 | * Common Development and Distribution License (the "License"). 7 | * You may not use this file except in compliance with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2013 Pagoda Box, Inc. All rights reserved. 24 | */ 25 | 26 | #include "narc.h" 27 | #include "stream.h" 28 | #include "sds.h" /* dynamic safe strings */ 29 | 30 | // temporary 31 | #include "tcp_client.h" 32 | 33 | #include /* standard buffered input/output */ 34 | #include /* standard library definitions */ 35 | #include /* standard symbolic constants and types */ 36 | #include 37 | #include 38 | #include 39 | #include /* Event driven programming library */ 40 | #include /* string operations */ 41 | 42 | /*============================ Utility functions ============================ */ 43 | 44 | int 45 | file_exists(char *filename) 46 | { 47 | struct stat buffer; 48 | return (stat(filename, &buffer) == 0); 49 | } 50 | 51 | void 52 | init_buffer(uv_buf_t buffer[]) 53 | { 54 | int i; 55 | for (i = 0; i < NARC_STREAM_BUFFERS; i++) { 56 | buffer[i].base = malloc(NARC_MAX_BUFF_SIZE); 57 | buffer[i].len = NARC_MAX_BUFF_SIZE - 1; 58 | memset(buffer[i].base, '\0', NARC_MAX_BUFF_SIZE); 59 | } 60 | } 61 | 62 | void 63 | free_buffer(uv_buf_t buffer[]) 64 | { 65 | int i; 66 | for (i = 0; i < NARC_STREAM_BUFFERS; i++) { 67 | free(buffer[i].base); 68 | } 69 | } 70 | 71 | void 72 | init_line(char *line) 73 | { 74 | memset(line, '\0', NARC_MAX_MESSAGE_SIZE); 75 | } 76 | 77 | void 78 | lock_stream(narc_stream *stream) 79 | { 80 | stream->lock = NARC_STREAM_LOCKED; 81 | } 82 | 83 | int 84 | stream_locked(narc_stream *stream) 85 | { 86 | return (stream->lock == NARC_STREAM_LOCKED); 87 | } 88 | 89 | void 90 | unlock_stream(narc_stream *stream) 91 | { 92 | stream->lock = NARC_STREAM_UNLOCKED; 93 | } 94 | 95 | int 96 | stream_unlocked(narc_stream *stream) 97 | { 98 | return (stream->lock == NARC_STREAM_UNLOCKED); 99 | } 100 | 101 | void 102 | submit_message(narc_stream *stream, char *message) 103 | { 104 | if (stream->rate_count < server.rate_limit) { 105 | if (stream->missed_count > 0) { 106 | char str[81]; 107 | sprintf(&str[0], "Suppressed %d messages due to rate limiting", stream->missed_count); 108 | stream->rate_count++; 109 | start_rate_limit_timer(stream); 110 | handle_message(stream->id, &str[0]); 111 | stream->missed_count = 0; 112 | } 113 | stream->rate_count++; 114 | start_rate_limit_timer(stream); 115 | handle_message(stream->id, message); 116 | } else { 117 | stream->missed_count++; 118 | } 119 | } 120 | 121 | /*============================== Callbacks ================================= */ 122 | 123 | void 124 | handle_file_open(uv_fs_t *req) 125 | { 126 | narc_stream *stream = req->data; 127 | 128 | if (req->result < 0) { 129 | narc_log(NARC_WARNING, "Error opening %s (%d/%d): %s", 130 | stream->file, 131 | stream->attempts, 132 | server.max_open_attempts, 133 | uv_err_name(req->result)); 134 | 135 | if (stream->attempts == server.max_open_attempts) 136 | narc_log(NARC_WARNING, "Reached max open attempts: %s", stream->file); 137 | else 138 | start_file_open_timer(stream); 139 | } else { 140 | narc_log(NARC_WARNING, "File opened: %s", stream->file); 141 | 142 | stream->fd = req->result; 143 | stream->attempts = 0; 144 | 145 | start_file_watcher(stream); 146 | start_file_stat(stream); 147 | } 148 | 149 | uv_fs_req_cleanup(req); 150 | free(req); 151 | } 152 | 153 | void 154 | handle_file_open_timeout(uv_timer_t* timer) 155 | { 156 | narc_stream *stream = (narc_stream *)timer->data; 157 | // uv_timer_stop(stream->open_timer); 158 | uv_close((uv_handle_t *)stream->open_timer, (uv_close_cb)free); 159 | // free(stream->open_timer); 160 | stream->open_timer = NULL; 161 | start_file_open(stream); 162 | } 163 | 164 | void 165 | handle_file_change(uv_fs_event_t *handle, const char *filename, int events, int status) 166 | { 167 | 168 | narc_stream *stream = handle->data; 169 | 170 | if ((events & UV_RENAME) == UV_RENAME) { 171 | narc_log(NARC_WARNING, "File renamed"); 172 | // File is being rotated 173 | uv_fs_t close_req; 174 | uv_fs_close(server.loop, &close_req, stream->fd, NULL); 175 | // uv_fs_event_stop(stream->fs_events); 176 | uv_close((uv_handle_t *)stream->fs_events, (uv_close_cb)free); 177 | // free(stream->fs_events); 178 | stream->fs_events = NULL; 179 | start_file_open(stream); 180 | } else if ((events & UV_CHANGE) == UV_CHANGE) { 181 | if (file_exists(stream->file)) { 182 | start_file_stat(stream); 183 | } else { 184 | narc_log(NARC_WARNING, "File deleted: %s, attempting to re-open", stream->file); 185 | uv_fs_t close_req; 186 | uv_fs_close(server.loop, &close_req, stream->fd, NULL); 187 | // uv_fs_event_stop(stream->fs_events); 188 | uv_close((uv_handle_t *)stream->fs_events, (uv_close_cb)free); 189 | // free(stream->fs_events); 190 | stream->fs_events = NULL; 191 | start_file_open(stream); 192 | } 193 | } 194 | } 195 | 196 | void 197 | handle_file_stat(uv_fs_t* req) 198 | { 199 | narc_stream *stream = req->data; 200 | if (req->result >= 0) { 201 | uv_stat_t *stat = req->ptr; 202 | 203 | // file is initially opened 204 | if (stream->size < 0){ 205 | stream->offset = stat->st_size; 206 | } 207 | 208 | // file has been truncated 209 | if ((long int)stat->st_size < (long int)stream->size){ 210 | stream->offset = 0; 211 | } 212 | 213 | // does the file need to be truncated? 214 | if ((long int)stat->st_size > (long int)server.truncate_limit){ 215 | stream->truncate = 1; 216 | } 217 | 218 | stream->size = stat->st_size; 219 | 220 | start_file_read(stream); 221 | } else { 222 | // there was an error, try things again? 223 | uv_fs_t close_req; 224 | uv_fs_close(server.loop, &close_req, stream->fd, NULL); 225 | // uv_fs_event_stop(stream->fs_events); 226 | uv_close((uv_handle_t *)stream->fs_events, (uv_close_cb)free); 227 | // free(stream->fs_events); 228 | stream->fs_events = NULL; 229 | start_file_open(stream); 230 | } 231 | 232 | uv_fs_req_cleanup(req); 233 | free(req); 234 | } 235 | 236 | void 237 | handle_file_read(uv_fs_t *req) 238 | { 239 | narc_stream *stream = req->data; 240 | 241 | if (req->result < 0) 242 | narc_log(NARC_WARNING, "Read error (%s): %s", stream->file, uv_err_name(req->result)); 243 | 244 | if (req->result > 0) { 245 | stream->offset += req->result; 246 | int i; 247 | for (i = 0; i < req->result; i++) { 248 | if (stream->index == 0) 249 | init_line(stream->current_line); 250 | 251 | if (stream->buffer->base[i] == '\n' || stream->index == NARC_MAX_MESSAGE_SIZE -1) { 252 | stream->current_line[stream->index] = '\0'; 253 | 254 | if (strcmp(stream->current_line, stream->previous_line) == 0 ) { 255 | stream->repeat_count++; 256 | init_line(stream->current_line); 257 | stream->index = 0; 258 | if (stream->repeat_count % 500 == 0) { 259 | char str[NARC_MAX_LOGMSG_LEN + 20]; 260 | sprintf(&str[0], "Previous message repeated %d times", stream->repeat_count); 261 | submit_message(stream, &str[0]); 262 | } 263 | continue; 264 | } else if (stream->repeat_count == 1) { 265 | submit_message(stream, stream->previous_line); 266 | } else if (stream->repeat_count > 1) { 267 | char str[NARC_MAX_LOGMSG_LEN + 20]; 268 | sprintf(&str[0], "Previous message repeated %d times", stream->repeat_count); 269 | submit_message(stream, &str[0]); 270 | } 271 | 272 | submit_message(stream, stream->current_line); 273 | stream->repeat_count = 0; 274 | 275 | char *tmp = stream->previous_line; 276 | stream->previous_line = stream->current_line; 277 | stream->current_line = tmp; 278 | 279 | stream->index = 0; 280 | } else { 281 | stream->current_line[stream->index] = stream->buffer->base[i]; 282 | stream->index += 1; 283 | } 284 | } 285 | } 286 | 287 | if (stream->truncate == 1) { 288 | if (truncate(stream->file, 0) == -1) { 289 | narc_log(NARC_WARNING, "Truncate error (%s): %s", stream->file, strerror(errno)); 290 | } 291 | stream->truncate = 0; 292 | } 293 | 294 | unlock_stream(stream); 295 | 296 | if (req->result == NARC_MAX_BUFF_SIZE -1) 297 | start_file_read(stream); 298 | 299 | uv_fs_req_cleanup(req); 300 | free(req); 301 | } 302 | 303 | void 304 | handle_rate_limit_timer(uv_timer_t* timer) 305 | { 306 | narc_stream *stream = timer->data; 307 | stream->rate_count--; 308 | // uv_timer_stop(timer); 309 | uv_close((uv_handle_t *)timer, (uv_close_cb)free); 310 | // free(timer); 311 | } 312 | 313 | /*================================= Watchers =================================== */ 314 | 315 | void 316 | start_file_open(narc_stream *stream) 317 | { 318 | narc_log(NARC_WARNING, "opening file %s", stream->file); 319 | uv_fs_t *req = malloc(sizeof(uv_fs_t)); 320 | if (uv_fs_open(server.loop, req, stream->file, O_RDONLY, 0, handle_file_open) == 0) { 321 | req->data = (void *)stream; 322 | stream->attempts += 1; 323 | } 324 | } 325 | 326 | void 327 | start_file_watcher(narc_stream *stream) 328 | { 329 | stream->fs_events = malloc(sizeof(uv_fs_event_t)); 330 | uv_fs_event_init(server.loop, stream->fs_events); 331 | if (uv_fs_event_start(stream->fs_events, handle_file_change, stream->file, 0) == 0) 332 | stream->fs_events->data = (void *)stream; 333 | } 334 | 335 | void 336 | start_file_open_timer(narc_stream *stream) 337 | { 338 | stream->open_timer = malloc(sizeof(uv_timer_t)); 339 | if (uv_timer_init(server.loop, stream->open_timer) == 0) { 340 | if (uv_timer_start(stream->open_timer, handle_file_open_timeout, server.open_retry_delay, 0) == 0) 341 | stream->open_timer->data = (void *)stream; 342 | } 343 | } 344 | 345 | void 346 | start_file_stat(narc_stream *stream) 347 | { 348 | uv_fs_t *req = malloc(sizeof(uv_fs_t)); 349 | if (uv_fs_stat(server.loop, req, stream->file, handle_file_stat) == 0) 350 | req->data = (void *)stream; 351 | } 352 | 353 | void 354 | start_file_read(narc_stream *stream) 355 | { 356 | if (stream_locked(stream)){ 357 | return; 358 | } 359 | 360 | uv_fs_t *req = malloc(sizeof(uv_fs_t)); 361 | if (uv_fs_read(server.loop, req, stream->fd, stream->buffer, NARC_STREAM_BUFFERS, stream->offset, handle_file_read) == 0) { 362 | lock_stream(stream); 363 | req->data = (void *)stream; 364 | } 365 | } 366 | 367 | void 368 | start_rate_limit_timer(narc_stream *stream) 369 | { 370 | uv_timer_t *timer = malloc(sizeof(uv_timer_t)); 371 | if (uv_timer_init(server.loop, timer) == 0) { 372 | if (uv_timer_start(timer, handle_rate_limit_timer, server.rate_time, 0) == 0) 373 | timer->data = (void *)stream; 374 | } 375 | } 376 | 377 | /*================================= API =================================== */ 378 | 379 | narc_stream 380 | *new_stream(char *id, char *file) 381 | { 382 | narc_stream *stream = malloc(sizeof(narc_stream)); 383 | 384 | stream->id = id; 385 | stream->file = file; 386 | stream->attempts = 0; 387 | stream->size = -1; 388 | stream->index = 0; 389 | stream->lock = NARC_STREAM_UNLOCKED; 390 | stream->rate_count = 0; 391 | stream->missed_count = 0; 392 | stream->repeat_count = 0; 393 | stream->message_header_size = strlen(id) + strlen(server.stream_id) + 24; 394 | stream->offset = 0; 395 | stream->fs_events = NULL; 396 | stream->open_timer = NULL; 397 | 398 | stream->current_line = &stream->line[0]; 399 | stream->previous_line = &stream->line[NARC_MAX_LOGMSG_LEN + 1]; 400 | init_line(stream->current_line); 401 | init_line(stream->previous_line); 402 | 403 | init_buffer(stream->buffer); 404 | 405 | return stream; 406 | } 407 | 408 | void 409 | stop_stream(narc_stream *stream) 410 | { 411 | if (stream->fs_events != NULL) { 412 | // uv_fs_event_stop(stream->fs_events); 413 | uv_close((uv_handle_t *)stream->fs_events, (uv_close_cb)free); 414 | // free(stream->fs_events); 415 | stream->fs_events = NULL; 416 | } 417 | if (stream->open_timer != NULL) { 418 | // uv_timer_stop(stream->open_timer); 419 | uv_close((uv_handle_t *)stream->open_timer, (uv_close_cb)free); 420 | // free(stream->open_timer); 421 | stream->open_timer = NULL; 422 | } 423 | } 424 | 425 | void 426 | free_stream(void *ptr) 427 | { 428 | narc_stream *stream = (narc_stream *)ptr; 429 | // stop_stream(stream); 430 | free_buffer(stream->buffer); 431 | sdsfree(stream->id); 432 | sdsfree(stream->file); 433 | free(stream); 434 | } 435 | 436 | void 437 | init_stream(narc_stream *stream) 438 | { 439 | start_file_open(stream); 440 | } 441 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "fmacros.h" 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "util.h" 42 | 43 | /* Glob-style pattern matching. */ 44 | int stringmatchlen(const char *pattern, int patternLen, 45 | const char *string, int stringLen, int nocase) 46 | { 47 | while(patternLen) { 48 | switch(pattern[0]) { 49 | case '*': 50 | while (pattern[1] == '*') { 51 | pattern++; 52 | patternLen--; 53 | } 54 | if (patternLen == 1) 55 | return 1; /* match */ 56 | while(stringLen) { 57 | if (stringmatchlen(pattern+1, patternLen-1, 58 | string, stringLen, nocase)) 59 | return 1; /* match */ 60 | string++; 61 | stringLen--; 62 | } 63 | return 0; /* no match */ 64 | break; 65 | case '?': 66 | if (stringLen == 0) 67 | return 0; /* no match */ 68 | string++; 69 | stringLen--; 70 | break; 71 | case '[': 72 | { 73 | int not, match; 74 | 75 | pattern++; 76 | patternLen--; 77 | not = pattern[0] == '^'; 78 | if (not) { 79 | pattern++; 80 | patternLen--; 81 | } 82 | match = 0; 83 | while(1) { 84 | if (pattern[0] == '\\') { 85 | pattern++; 86 | patternLen--; 87 | if (pattern[0] == string[0]) 88 | match = 1; 89 | } else if (pattern[0] == ']') { 90 | break; 91 | } else if (patternLen == 0) { 92 | pattern--; 93 | patternLen++; 94 | break; 95 | } else if (pattern[1] == '-' && patternLen >= 3) { 96 | int start = pattern[0]; 97 | int end = pattern[2]; 98 | int c = string[0]; 99 | if (start > end) { 100 | int t = start; 101 | start = end; 102 | end = t; 103 | } 104 | if (nocase) { 105 | start = tolower(start); 106 | end = tolower(end); 107 | c = tolower(c); 108 | } 109 | pattern += 2; 110 | patternLen -= 2; 111 | if (c >= start && c <= end) 112 | match = 1; 113 | } else { 114 | if (!nocase) { 115 | if (pattern[0] == string[0]) 116 | match = 1; 117 | } else { 118 | if (tolower((int)pattern[0]) == tolower((int)string[0])) 119 | match = 1; 120 | } 121 | } 122 | pattern++; 123 | patternLen--; 124 | } 125 | if (not) 126 | match = !match; 127 | if (!match) 128 | return 0; /* no match */ 129 | string++; 130 | stringLen--; 131 | break; 132 | } 133 | case '\\': 134 | if (patternLen >= 2) { 135 | pattern++; 136 | patternLen--; 137 | } 138 | /* fall through */ 139 | default: 140 | if (!nocase) { 141 | if (pattern[0] != string[0]) 142 | return 0; /* no match */ 143 | } else { 144 | if (tolower((int)pattern[0]) != tolower((int)string[0])) 145 | return 0; /* no match */ 146 | } 147 | string++; 148 | stringLen--; 149 | break; 150 | } 151 | pattern++; 152 | patternLen--; 153 | if (stringLen == 0) { 154 | while(*pattern == '*') { 155 | pattern++; 156 | patternLen--; 157 | } 158 | break; 159 | } 160 | } 161 | if (patternLen == 0 && stringLen == 0) 162 | return 1; 163 | return 0; 164 | } 165 | 166 | int stringmatch(const char *pattern, const char *string, int nocase) { 167 | return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase); 168 | } 169 | 170 | /* Convert a string representing an amount of memory into the number of 171 | * bytes, so for instance memtoll("1Gi") will return 1073741824 that is 172 | * (1024*1024*1024). 173 | * 174 | * On parsing error, if *err is not NULL, it's set to 1, otherwise it's 175 | * set to 0 */ 176 | long long memtoll(const char *p, int *err) { 177 | const char *u; 178 | char buf[128]; 179 | long mul; /* unit multiplier */ 180 | long long val; 181 | unsigned int digits; 182 | 183 | if (err) *err = 0; 184 | /* Search the first non digit character. */ 185 | u = p; 186 | if (*u == '-') u++; 187 | while(*u && isdigit(*u)) u++; 188 | if (*u == '\0' || !strcasecmp(u,"b")) { 189 | mul = 1; 190 | } else if (!strcasecmp(u,"k")) { 191 | mul = 1000; 192 | } else if (!strcasecmp(u,"kb")) { 193 | mul = 1024; 194 | } else if (!strcasecmp(u,"m")) { 195 | mul = 1000*1000; 196 | } else if (!strcasecmp(u,"mb")) { 197 | mul = 1024*1024; 198 | } else if (!strcasecmp(u,"g")) { 199 | mul = 1000L*1000*1000; 200 | } else if (!strcasecmp(u,"gb")) { 201 | mul = 1024L*1024*1024; 202 | } else { 203 | if (err) *err = 1; 204 | mul = 1; 205 | } 206 | digits = u-p; 207 | if (digits >= sizeof(buf)) { 208 | if (err) *err = 1; 209 | return LLONG_MAX; 210 | } 211 | memcpy(buf,p,digits); 212 | buf[digits] = '\0'; 213 | val = strtoll(buf,NULL,10); 214 | return val*mul; 215 | } 216 | 217 | /* Convert a long long into a string. Returns the number of 218 | * characters needed to represent the number, that can be shorter if passed 219 | * buffer length is not enough to store the whole number. */ 220 | int ll2string(char *s, size_t len, long long value) { 221 | char buf[32], *p; 222 | unsigned long long v; 223 | size_t l; 224 | 225 | if (len == 0) return 0; 226 | v = (value < 0) ? -value : value; 227 | p = buf+31; /* point to the last character */ 228 | do { 229 | *p-- = '0'+(v%10); 230 | v /= 10; 231 | } while(v); 232 | if (value < 0) *p-- = '-'; 233 | p++; 234 | l = 32-(p-buf); 235 | if (l+1 > len) l = len-1; /* Make sure it fits, including the nul term */ 236 | memcpy(s,p,l); 237 | s[l] = '\0'; 238 | return l; 239 | } 240 | 241 | /* Convert a string into a long long. Returns 1 if the string could be parsed 242 | * into a (non-overflowing) long long, 0 otherwise. The value will be set to 243 | * the parsed value when appropriate. */ 244 | int string2ll(const char *s, size_t slen, long long *value) { 245 | const char *p = s; 246 | size_t plen = 0; 247 | int negative = 0; 248 | unsigned long long v; 249 | 250 | if (plen == slen) 251 | return 0; 252 | 253 | /* Special case: first and only digit is 0. */ 254 | if (slen == 1 && p[0] == '0') { 255 | if (value != NULL) *value = 0; 256 | return 1; 257 | } 258 | 259 | if (p[0] == '-') { 260 | negative = 1; 261 | p++; plen++; 262 | 263 | /* Abort on only a negative sign. */ 264 | if (plen == slen) 265 | return 0; 266 | } 267 | 268 | /* First digit should be 1-9, otherwise the string should just be 0. */ 269 | if (p[0] >= '1' && p[0] <= '9') { 270 | v = p[0]-'0'; 271 | p++; plen++; 272 | } else if (p[0] == '0' && slen == 1) { 273 | *value = 0; 274 | return 1; 275 | } else { 276 | return 0; 277 | } 278 | 279 | while (plen < slen && p[0] >= '0' && p[0] <= '9') { 280 | if (v > (ULLONG_MAX / 10)) /* Overflow. */ 281 | return 0; 282 | v *= 10; 283 | 284 | if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ 285 | return 0; 286 | v += p[0]-'0'; 287 | 288 | p++; plen++; 289 | } 290 | 291 | /* Return if not all bytes were used. */ 292 | if (plen < slen) 293 | return 0; 294 | 295 | if (negative) { 296 | if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ 297 | return 0; 298 | if (value != NULL) *value = -v; 299 | } else { 300 | if (v > LLONG_MAX) /* Overflow. */ 301 | return 0; 302 | if (value != NULL) *value = v; 303 | } 304 | return 1; 305 | } 306 | 307 | /* Convert a string into a long. Returns 1 if the string could be parsed into a 308 | * (non-overflowing) long, 0 otherwise. The value will be set to the parsed 309 | * value when appropriate. */ 310 | int string2l(const char *s, size_t slen, long *lval) { 311 | long long llval; 312 | 313 | if (!string2ll(s,slen,&llval)) 314 | return 0; 315 | 316 | if (llval < LONG_MIN || llval > LONG_MAX) 317 | return 0; 318 | 319 | *lval = (long)llval; 320 | return 1; 321 | } 322 | 323 | /* Given the filename, return the absolute path as an SDS string, or NULL 324 | * if it fails for some reason. Note that "filename" may be an absolute path 325 | * already, this will be detected and handled correctly. 326 | * 327 | * The function does not try to normalize everything, but only the obvious 328 | * case of one or more "../" appearning at the start of "filename" 329 | * relative path. */ 330 | sds getAbsolutePath(char *filename) { 331 | char cwd[1024]; 332 | sds abspath; 333 | sds relpath = sdsnew(filename); 334 | 335 | relpath = sdstrim(relpath," \r\n\t"); 336 | if (relpath[0] == '/') return relpath; /* Path is already absolute. */ 337 | 338 | /* If path is relative, join cwd and relative path. */ 339 | if (getcwd(cwd,sizeof(cwd)) == NULL) { 340 | sdsfree(relpath); 341 | return NULL; 342 | } 343 | abspath = sdsnew(cwd); 344 | if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/') 345 | abspath = sdscat(abspath,"/"); 346 | 347 | /* At this point we have the current path always ending with "/", and 348 | * the trimmed relative path. Try to normalize the obvious case of 349 | * trailing ../ elements at the start of the path. 350 | * 351 | * For every "../" we find in the filename, we remove it and also remove 352 | * the last element of the cwd, unless the current cwd is "/". */ 353 | while (sdslen(relpath) >= 3 && 354 | relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/') 355 | { 356 | sdsrange(relpath,3,-1); 357 | if (sdslen(abspath) > 1) { 358 | char *p = abspath + sdslen(abspath)-2; 359 | int trimlen = 1; 360 | 361 | while(*p != '/') { 362 | p--; 363 | trimlen++; 364 | } 365 | sdsrange(abspath,0,-(trimlen+1)); 366 | } 367 | } 368 | 369 | /* Finally glue the two parts together. */ 370 | abspath = sdscatsds(abspath,relpath); 371 | sdsfree(relpath); 372 | return abspath; 373 | } 374 | 375 | /* Return true if the specified path is just a file basename without any 376 | * relative or absolute path. This function just checks that no / or \ 377 | * character exists inside the specified path, that's enough in the 378 | * environments where Redis runs. */ 379 | int pathIsBaseName(char *path) { 380 | return strchr(path,'/') == NULL && strchr(path,'\\') == NULL; 381 | } 382 | 383 | #ifdef UTIL_TEST_MAIN 384 | #include 385 | 386 | void test_string2ll(void) { 387 | char buf[32]; 388 | long long v; 389 | 390 | /* May not start with +. */ 391 | strcpy(buf,"+1"); 392 | assert(string2ll(buf,strlen(buf),&v) == 0); 393 | 394 | /* Leading space. */ 395 | strcpy(buf," 1"); 396 | assert(string2ll(buf,strlen(buf),&v) == 0); 397 | 398 | /* Trailing space. */ 399 | strcpy(buf,"1 "); 400 | assert(string2ll(buf,strlen(buf),&v) == 0); 401 | 402 | /* May not start with 0. */ 403 | strcpy(buf,"01"); 404 | assert(string2ll(buf,strlen(buf),&v) == 0); 405 | 406 | strcpy(buf,"-1"); 407 | assert(string2ll(buf,strlen(buf),&v) == 1); 408 | assert(v == -1); 409 | 410 | strcpy(buf,"0"); 411 | assert(string2ll(buf,strlen(buf),&v) == 1); 412 | assert(v == 0); 413 | 414 | strcpy(buf,"1"); 415 | assert(string2ll(buf,strlen(buf),&v) == 1); 416 | assert(v == 1); 417 | 418 | strcpy(buf,"99"); 419 | assert(string2ll(buf,strlen(buf),&v) == 1); 420 | assert(v == 99); 421 | 422 | strcpy(buf,"-99"); 423 | assert(string2ll(buf,strlen(buf),&v) == 1); 424 | assert(v == -99); 425 | 426 | strcpy(buf,"-9223372036854775808"); 427 | assert(string2ll(buf,strlen(buf),&v) == 1); 428 | assert(v == LLONG_MIN); 429 | 430 | strcpy(buf,"-9223372036854775809"); /* overflow */ 431 | assert(string2ll(buf,strlen(buf),&v) == 0); 432 | 433 | strcpy(buf,"9223372036854775807"); 434 | assert(string2ll(buf,strlen(buf),&v) == 1); 435 | assert(v == LLONG_MAX); 436 | 437 | strcpy(buf,"9223372036854775808"); /* overflow */ 438 | assert(string2ll(buf,strlen(buf),&v) == 0); 439 | } 440 | 441 | void test_string2l(void) { 442 | char buf[32]; 443 | long v; 444 | 445 | /* May not start with +. */ 446 | strcpy(buf,"+1"); 447 | assert(string2l(buf,strlen(buf),&v) == 0); 448 | 449 | /* May not start with 0. */ 450 | strcpy(buf,"01"); 451 | assert(string2l(buf,strlen(buf),&v) == 0); 452 | 453 | strcpy(buf,"-1"); 454 | assert(string2l(buf,strlen(buf),&v) == 1); 455 | assert(v == -1); 456 | 457 | strcpy(buf,"0"); 458 | assert(string2l(buf,strlen(buf),&v) == 1); 459 | assert(v == 0); 460 | 461 | strcpy(buf,"1"); 462 | assert(string2l(buf,strlen(buf),&v) == 1); 463 | assert(v == 1); 464 | 465 | strcpy(buf,"99"); 466 | assert(string2l(buf,strlen(buf),&v) == 1); 467 | assert(v == 99); 468 | 469 | strcpy(buf,"-99"); 470 | assert(string2l(buf,strlen(buf),&v) == 1); 471 | assert(v == -99); 472 | 473 | #if LONG_MAX != LLONG_MAX 474 | strcpy(buf,"-2147483648"); 475 | assert(string2l(buf,strlen(buf),&v) == 1); 476 | assert(v == LONG_MIN); 477 | 478 | strcpy(buf,"-2147483649"); /* overflow */ 479 | assert(string2l(buf,strlen(buf),&v) == 0); 480 | 481 | strcpy(buf,"2147483647"); 482 | assert(string2l(buf,strlen(buf),&v) == 1); 483 | assert(v == LONG_MAX); 484 | 485 | strcpy(buf,"2147483648"); /* overflow */ 486 | assert(string2l(buf,strlen(buf),&v) == 0); 487 | #endif 488 | } 489 | 490 | int main(int argc, char **argv) { 491 | test_string2ll(); 492 | test_string2l(); 493 | return 0; 494 | } 495 | #endif 496 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | -------------------------------------------------------------------------------- /src/sds.c: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2012, 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 | #include 34 | #include 35 | #include 36 | #include "sds.h" 37 | // #include "malloc.h" 38 | 39 | /* Create a new sds string with the content specified by the 'init' pointer 40 | * and 'initlen'. 41 | * If NULL is used for 'init' the string is initialized with zero bytes. 42 | * 43 | * The string is always null-termined (all the sds strings are, always) so 44 | * even if you create an sds string with: 45 | * 46 | * mystring = sdsnewlen("abc",3"); 47 | * 48 | * You can print the string with printf() as there is an implicit \0 at the 49 | * end of the string. However the string is binary safe and can contain 50 | * \0 characters in the middle, as the length is stored in the sds header. */ 51 | sds sdsnewlen(const void *init, size_t initlen) { 52 | struct sdshdr *sh; 53 | 54 | if (init) { 55 | sh = malloc(sizeof(struct sdshdr)+initlen+1); 56 | } else { 57 | sh = calloc(1,sizeof(struct sdshdr)+initlen+1); 58 | } 59 | if (sh == NULL) return NULL; 60 | sh->len = initlen; 61 | sh->free = 0; 62 | if (initlen && init) 63 | memcpy(sh->buf, init, initlen); 64 | sh->buf[initlen] = '\0'; 65 | return (char*)sh->buf; 66 | } 67 | 68 | /* Create an empty (zero length) sds string. Even in this case the string 69 | * always has an implicit null term. */ 70 | sds sdsempty(void) { 71 | return sdsnewlen("",0); 72 | } 73 | 74 | /* Create a new sds string starting from a null termined C string. */ 75 | sds sdsnew(const char *init) { 76 | size_t initlen = (init == NULL) ? 0 : strlen(init); 77 | return sdsnewlen(init, initlen); 78 | } 79 | 80 | /* Duplicate an sds string. */ 81 | sds sdsdup(const sds s) { 82 | return sdsnewlen(s, sdslen(s)); 83 | } 84 | 85 | /* Free an sds string. No operation is performed if 's' is NULL. */ 86 | void sdsfree(sds s) { 87 | if (s == NULL) return; 88 | free(s-sizeof(struct sdshdr)); 89 | } 90 | 91 | /* Set the sds string length to the length as obtained with strlen(), so 92 | * considering as content only up to the first null term character. 93 | * 94 | * This function is useful when the sds string is hacked manually in some 95 | * way, like in the following example: 96 | * 97 | * s = sdsnew("foobar"); 98 | * s[2] = '\0'; 99 | * sdsupdatelen(s); 100 | * printf("%d\n", sdslen(s)); 101 | * 102 | * The output will be "2", but if we comment out the call to sdsupdatelen() 103 | * the output will be "6" as the string was modified but the logical length 104 | * remains 6 bytes. */ 105 | void sdsupdatelen(sds s) { 106 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 107 | int reallen = strlen(s); 108 | sh->free += (sh->len-reallen); 109 | sh->len = reallen; 110 | } 111 | 112 | /* Modify an sds string on-place to make it empty (zero length). 113 | * However all the existing buffer is not discarded but set as free space 114 | * so that next append operations will not require allocations up to the 115 | * number of bytes previously available. */ 116 | void sdsclear(sds s) { 117 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 118 | sh->free += sh->len; 119 | sh->len = 0; 120 | sh->buf[0] = '\0'; 121 | } 122 | 123 | /* Enlarge the free space at the end of the sds string so that the caller 124 | * is sure that after calling this function can overwrite up to addlen 125 | * bytes after the end of the string, plus one more byte for nul term. 126 | * 127 | * Note: this does not change the *length* of the sds string as returned 128 | * by sdslen(), but only the free buffer space we have. */ 129 | sds sdsMakeRoomFor(sds s, size_t addlen) { 130 | struct sdshdr *sh, *newsh; 131 | size_t free = sdsavail(s); 132 | size_t len, newlen; 133 | 134 | if (free >= addlen) return s; 135 | len = sdslen(s); 136 | sh = (void*) (s-(sizeof(struct sdshdr))); 137 | newlen = (len+addlen); 138 | if (newlen < SDS_MAX_PREALLOC) 139 | newlen *= 2; 140 | else 141 | newlen += SDS_MAX_PREALLOC; 142 | newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1); 143 | if (newsh == NULL) return NULL; 144 | 145 | newsh->free = newlen - len; 146 | return newsh->buf; 147 | } 148 | 149 | /* Reallocate the sds string so that it has no free space at the end. The 150 | * contained string remains not altered, but next concatenation operations 151 | * will require a reallocation. 152 | * 153 | * After the call, the passed sds string is no longer valid and all the 154 | * references must be substituted with the new pointer returned by the call. */ 155 | sds sdsRemoveFreeSpace(sds s) { 156 | struct sdshdr *sh; 157 | 158 | sh = (void*) (s-(sizeof(struct sdshdr))); 159 | sh = realloc(sh, sizeof(struct sdshdr)+sh->len+1); 160 | sh->free = 0; 161 | return sh->buf; 162 | } 163 | 164 | /* Return the total size of the allocation of the specifed sds string, 165 | * including: 166 | * 1) The sds header before the pointer. 167 | * 2) The string. 168 | * 3) The free buffer at the end if any. 169 | * 4) The implicit null term. 170 | */ 171 | size_t sdsAllocSize(sds s) { 172 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 173 | 174 | return sizeof(*sh)+sh->len+sh->free+1; 175 | } 176 | 177 | /* Increment the sds length and decrements the left free space at the 178 | * end of the string according to 'incr'. Also set the null term 179 | * in the new end of the string. 180 | * 181 | * This function is used in order to fix the string length after the 182 | * user calls sdsMakeRoomFor(), writes something after the end of 183 | * the current string, and finally needs to set the new length. 184 | * 185 | * Note: it is possible to use a negative increment in order to 186 | * right-trim the string. 187 | * 188 | * Usage example: 189 | * 190 | * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the 191 | * following schema, to cat bytes coming from the kernel to the end of an 192 | * sds string without copying into an intermediate buffer: 193 | * 194 | * oldlen = sdslen(s); 195 | * s = sdsMakeRoomFor(s, BUFFER_SIZE); 196 | * nread = read(fd, s+oldlen, BUFFER_SIZE); 197 | * ... check for nread <= 0 and handle it ... 198 | * sdsIncrLen(s, nread); 199 | */ 200 | void sdsIncrLen(sds s, int incr) { 201 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 202 | 203 | assert(sh->free >= incr); 204 | sh->len += incr; 205 | sh->free -= incr; 206 | assert(sh->free >= 0); 207 | s[sh->len] = '\0'; 208 | } 209 | 210 | /* Grow the sds to have the specified length. Bytes that were not part of 211 | * the original length of the sds will be set to zero. 212 | * 213 | * if the specified length is smaller than the current length, no operation 214 | * is performed. */ 215 | sds sdsgrowzero(sds s, size_t len) { 216 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 217 | size_t totlen, curlen = sh->len; 218 | 219 | if (len <= curlen) return s; 220 | s = sdsMakeRoomFor(s,len-curlen); 221 | if (s == NULL) return NULL; 222 | 223 | /* Make sure added region doesn't contain garbage */ 224 | sh = (void*)(s-(sizeof(struct sdshdr))); 225 | memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ 226 | totlen = sh->len+sh->free; 227 | sh->len = len; 228 | sh->free = totlen-sh->len; 229 | return s; 230 | } 231 | 232 | /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the 233 | * end of the specified sds string 's'. 234 | * 235 | * After the call, the passed sds string is no longer valid and all the 236 | * references must be substituted with the new pointer returned by the call. */ 237 | sds sdscatlen(sds s, const void *t, size_t len) { 238 | struct sdshdr *sh; 239 | size_t curlen = sdslen(s); 240 | 241 | s = sdsMakeRoomFor(s,len); 242 | if (s == NULL) return NULL; 243 | sh = (void*) (s-(sizeof(struct sdshdr))); 244 | memcpy(s+curlen, t, len); 245 | sh->len = curlen+len; 246 | sh->free = sh->free-len; 247 | s[curlen+len] = '\0'; 248 | return s; 249 | } 250 | 251 | /* Append the specified null termianted C string to the sds string 's'. 252 | * 253 | * After the call, the passed sds string is no longer valid and all the 254 | * references must be substituted with the new pointer returned by the call. */ 255 | sds sdscat(sds s, const char *t) { 256 | return sdscatlen(s, t, strlen(t)); 257 | } 258 | 259 | /* Append the specified sds 't' to the existing sds 's'. 260 | * 261 | * After the call, the modified sds string is no longer valid and all the 262 | * references must be substituted with the new pointer returned by the call. */ 263 | sds sdscatsds(sds s, const sds t) { 264 | return sdscatlen(s, t, sdslen(t)); 265 | } 266 | 267 | /* Destructively modify the sds string 's' to hold the specified binary 268 | * safe string pointed by 't' of length 'len' bytes. */ 269 | sds sdscpylen(sds s, const char *t, size_t len) { 270 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 271 | size_t totlen = sh->free+sh->len; 272 | 273 | if (totlen < len) { 274 | s = sdsMakeRoomFor(s,len-sh->len); 275 | if (s == NULL) return NULL; 276 | sh = (void*) (s-(sizeof(struct sdshdr))); 277 | totlen = sh->free+sh->len; 278 | } 279 | memcpy(s, t, len); 280 | s[len] = '\0'; 281 | sh->len = len; 282 | sh->free = totlen-len; 283 | return s; 284 | } 285 | 286 | /* Like sdscpylen() but 't' must be a null-termined string so that the length 287 | * of the string is obtained with strlen(). */ 288 | sds sdscpy(sds s, const char *t) { 289 | return sdscpylen(s, t, strlen(t)); 290 | } 291 | 292 | /* Like sdscatpritf() but gets va_list instead of being variadic. */ 293 | sds sdscatvprintf(sds s, const char *fmt, va_list ap) { 294 | va_list cpy; 295 | char *buf, *t; 296 | size_t buflen = 16; 297 | 298 | while(1) { 299 | buf = malloc(buflen); 300 | if (buf == NULL) return NULL; 301 | buf[buflen-2] = '\0'; 302 | va_copy(cpy,ap); 303 | vsnprintf(buf, buflen, fmt, cpy); 304 | if (buf[buflen-2] != '\0') { 305 | free(buf); 306 | buflen *= 2; 307 | continue; 308 | } 309 | break; 310 | } 311 | t = sdscat(s, buf); 312 | free(buf); 313 | return t; 314 | } 315 | 316 | /* Append to the sds string 's' a string obtained using printf-alike format 317 | * specifier. 318 | * 319 | * After the call, the modified sds string is no longer valid and all the 320 | * references must be substituted with the new pointer returned by the call. 321 | * 322 | * Example: 323 | * 324 | * s = sdsempty("Sum is: "); 325 | * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). 326 | * 327 | * Often you need to create a string from scratch with the printf-alike 328 | * format. When this is the need, just use sdsempty() as the target string: 329 | * 330 | * s = sdscatprintf(sdsempty(), "... your format ...", args); 331 | */ 332 | sds sdscatprintf(sds s, const char *fmt, ...) { 333 | va_list ap; 334 | char *t; 335 | va_start(ap, fmt); 336 | t = sdscatvprintf(s,fmt,ap); 337 | va_end(ap); 338 | return t; 339 | } 340 | 341 | /* Remove the part of the string from left and from right composed just of 342 | * contiguous characters found in 'cset', that is a null terminted C string. 343 | * 344 | * After the call, the modified sds string is no longer valid and all the 345 | * references must be substituted with the new pointer returned by the call. 346 | * 347 | * Example: 348 | * 349 | * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); 350 | * s = sdstrim(s,"A. :"); 351 | * printf("%s\n", s); 352 | * 353 | * Output will be just "Hello World". 354 | */ 355 | sds sdstrim(sds s, const char *cset) { 356 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 357 | char *start, *end, *sp, *ep; 358 | size_t len; 359 | 360 | sp = start = s; 361 | ep = end = s+sdslen(s)-1; 362 | while(sp <= end && strchr(cset, *sp)) sp++; 363 | while(ep > start && strchr(cset, *ep)) ep--; 364 | len = (sp > ep) ? 0 : ((ep-sp)+1); 365 | if (sh->buf != sp) memmove(sh->buf, sp, len); 366 | sh->buf[len] = '\0'; 367 | sh->free = sh->free+(sh->len-len); 368 | sh->len = len; 369 | return s; 370 | } 371 | 372 | /* Turn the string into a smaller (or equal) string containing only the 373 | * substring specified by the 'start' and 'end' indexes. 374 | * 375 | * start and end can be negative, where -1 means the last character of the 376 | * string, -2 the penultimate character, and so forth. 377 | * 378 | * The interval is inclusive, so the start and end characters will be part 379 | * of the resulting string. 380 | * 381 | * The string is modified in-place. 382 | * 383 | * Example: 384 | * 385 | * s = sdsnew("Hello World"); 386 | * sdstrim(s,1,-1); => "ello Worl" 387 | */ 388 | void sdsrange(sds s, int start, int end) { 389 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 390 | size_t newlen, len = sdslen(s); 391 | 392 | if (len == 0) return; 393 | if (start < 0) { 394 | start = len+start; 395 | if (start < 0) start = 0; 396 | } 397 | if (end < 0) { 398 | end = len+end; 399 | if (end < 0) end = 0; 400 | } 401 | newlen = (start > end) ? 0 : (end-start)+1; 402 | if (newlen != 0) { 403 | if (start >= (signed)len) { 404 | newlen = 0; 405 | } else if (end >= (signed)len) { 406 | end = len-1; 407 | newlen = (start > end) ? 0 : (end-start)+1; 408 | } 409 | } else { 410 | start = 0; 411 | } 412 | if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); 413 | sh->buf[newlen] = 0; 414 | sh->free = sh->free+(sh->len-newlen); 415 | sh->len = newlen; 416 | } 417 | 418 | /* Apply tolower() to every character of the sds string 's'. */ 419 | void sdstolower(sds s) { 420 | int len = sdslen(s), j; 421 | 422 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); 423 | } 424 | 425 | /* Apply toupper() to every character of the sds string 's'. */ 426 | void sdstoupper(sds s) { 427 | int len = sdslen(s), j; 428 | 429 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); 430 | } 431 | 432 | /* Compare two sds strings s1 and s2 with memcmp(). 433 | * 434 | * Return value: 435 | * 436 | * 1 if s1 > s2. 437 | * -1 if s1 < s2. 438 | * 0 if s1 and s2 are exactly the same binary string. 439 | * 440 | * If two strings share exactly the same prefix, but one of the two has 441 | * additional characters, the longer string is considered to be greater than 442 | * the smaller one. */ 443 | int sdscmp(const sds s1, const sds s2) { 444 | size_t l1, l2, minlen; 445 | int cmp; 446 | 447 | l1 = sdslen(s1); 448 | l2 = sdslen(s2); 449 | minlen = (l1 < l2) ? l1 : l2; 450 | cmp = memcmp(s1,s2,minlen); 451 | if (cmp == 0) return l1-l2; 452 | return cmp; 453 | } 454 | 455 | /* Split 's' with separator in 'sep'. An array 456 | * of sds strings is returned. *count will be set 457 | * by reference to the number of tokens returned. 458 | * 459 | * On out of memory, zero length string, zero length 460 | * separator, NULL is returned. 461 | * 462 | * Note that 'sep' is able to split a string using 463 | * a multi-character separator. For example 464 | * sdssplit("foo_-_bar","_-_"); will return two 465 | * elements "foo" and "bar". 466 | * 467 | * This version of the function is binary-safe but 468 | * requires length arguments. sdssplit() is just the 469 | * same function but for zero-terminated strings. 470 | */ 471 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { 472 | int elements = 0, slots = 5, start = 0, j; 473 | sds *tokens; 474 | 475 | if (seplen < 1 || len < 0) return NULL; 476 | 477 | tokens = malloc(sizeof(sds)*slots); 478 | if (tokens == NULL) return NULL; 479 | 480 | if (len == 0) { 481 | *count = 0; 482 | return tokens; 483 | } 484 | for (j = 0; j < (len-(seplen-1)); j++) { 485 | /* make sure there is room for the next element and the final one */ 486 | if (slots < elements+2) { 487 | sds *newtokens; 488 | 489 | slots *= 2; 490 | newtokens = realloc(tokens,sizeof(sds)*slots); 491 | if (newtokens == NULL) goto cleanup; 492 | tokens = newtokens; 493 | } 494 | /* search the separator */ 495 | if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { 496 | tokens[elements] = sdsnewlen(s+start,j-start); 497 | if (tokens[elements] == NULL) goto cleanup; 498 | elements++; 499 | start = j+seplen; 500 | j = j+seplen-1; /* skip the separator */ 501 | } 502 | } 503 | /* Add the final element. We are sure there is room in the tokens array. */ 504 | tokens[elements] = sdsnewlen(s+start,len-start); 505 | if (tokens[elements] == NULL) goto cleanup; 506 | elements++; 507 | *count = elements; 508 | return tokens; 509 | 510 | cleanup: 511 | { 512 | int i; 513 | for (i = 0; i < elements; i++) sdsfree(tokens[i]); 514 | free(tokens); 515 | *count = 0; 516 | return NULL; 517 | } 518 | } 519 | 520 | /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ 521 | void sdsfreesplitres(sds *tokens, int count) { 522 | if (!tokens) return; 523 | while(count--) 524 | sdsfree(tokens[count]); 525 | free(tokens); 526 | } 527 | 528 | /* Create an sds string from a long long value. It is much faster than: 529 | * 530 | * sdscatprintf(sdsempty(),"%lld\n", value); 531 | */ 532 | sds sdsfromlonglong(long long value) { 533 | char buf[32], *p; 534 | unsigned long long v; 535 | 536 | v = (value < 0) ? -value : value; 537 | p = buf+31; /* point to the last character */ 538 | do { 539 | *p-- = '0'+(v%10); 540 | v /= 10; 541 | } while(v); 542 | if (value < 0) *p-- = '-'; 543 | p++; 544 | return sdsnewlen(p,32-(p-buf)); 545 | } 546 | 547 | /* Append to the sds string "s" an escaped string representation where 548 | * all the non-printable characters (tested with isprint()) are turned into 549 | * escapes in the form "\n\r\a...." or "\x". 550 | * 551 | * After the call, the modified sds string is no longer valid and all the 552 | * references must be substituted with the new pointer returned by the call. */ 553 | sds sdscatrepr(sds s, const char *p, size_t len) { 554 | s = sdscatlen(s,"\"",1); 555 | while(len--) { 556 | switch(*p) { 557 | case '\\': 558 | case '"': 559 | s = sdscatprintf(s,"\\%c",*p); 560 | break; 561 | case '\n': s = sdscatlen(s,"\\n",2); break; 562 | case '\r': s = sdscatlen(s,"\\r",2); break; 563 | case '\t': s = sdscatlen(s,"\\t",2); break; 564 | case '\a': s = sdscatlen(s,"\\a",2); break; 565 | case '\b': s = sdscatlen(s,"\\b",2); break; 566 | default: 567 | if (isprint(*p)) 568 | s = sdscatprintf(s,"%c",*p); 569 | else 570 | s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); 571 | break; 572 | } 573 | p++; 574 | } 575 | return sdscatlen(s,"\"",1); 576 | } 577 | 578 | /* Helper function for sdssplitargs() that returns non zero if 'c' 579 | * is a valid hex digit. */ 580 | int is_hex_digit(char c) { 581 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 582 | (c >= 'A' && c <= 'F'); 583 | } 584 | 585 | /* Helper function for sdssplitargs() that converts an hex digit into an 586 | * integer from 0 to 15 */ 587 | int hex_digit_to_int(char c) { 588 | switch(c) { 589 | case '0': return 0; 590 | case '1': return 1; 591 | case '2': return 2; 592 | case '3': return 3; 593 | case '4': return 4; 594 | case '5': return 5; 595 | case '6': return 6; 596 | case '7': return 7; 597 | case '8': return 8; 598 | case '9': return 9; 599 | case 'a': case 'A': return 10; 600 | case 'b': case 'B': return 11; 601 | case 'c': case 'C': return 12; 602 | case 'd': case 'D': return 13; 603 | case 'e': case 'E': return 14; 604 | case 'f': case 'F': return 15; 605 | default: return 0; 606 | } 607 | } 608 | 609 | /* Split a line into arguments, where every argument can be in the 610 | * following programming-language REPL-alike form: 611 | * 612 | * foo bar "newline are supported\n" and "\xff\x00otherstuff" 613 | * 614 | * The number of arguments is stored into *argc, and an array 615 | * of sds is returned. 616 | * 617 | * The caller should free the resulting array of sds strings with 618 | * sdsfreesplitres(). 619 | * 620 | * Note that sdscatrepr() is able to convert back a string into 621 | * a quoted string in the same format sdssplitargs() is able to parse. 622 | * 623 | * The function returns the allocated tokens on success, even when the 624 | * input string is empty, or NULL if the input contains unbalanced 625 | * quotes or closed quotes followed by non space characters 626 | * as in: "foo"bar or "foo' 627 | */ 628 | sds *sdssplitargs(const char *line, int *argc) { 629 | const char *p = line; 630 | char *current = NULL; 631 | char **vector = NULL; 632 | 633 | *argc = 0; 634 | while(1) { 635 | /* skip blanks */ 636 | while(*p && isspace(*p)) p++; 637 | if (*p) { 638 | /* get a token */ 639 | int inq=0; /* set to 1 if we are in "quotes" */ 640 | int insq=0; /* set to 1 if we are in 'single quotes' */ 641 | int done=0; 642 | 643 | if (current == NULL) current = sdsempty(); 644 | while(!done) { 645 | if (inq) { 646 | if (*p == '\\' && *(p+1) == 'x' && 647 | is_hex_digit(*(p+2)) && 648 | is_hex_digit(*(p+3))) 649 | { 650 | unsigned char byte; 651 | 652 | byte = (hex_digit_to_int(*(p+2))*16)+ 653 | hex_digit_to_int(*(p+3)); 654 | current = sdscatlen(current,(char*)&byte,1); 655 | p += 3; 656 | } else if (*p == '\\' && *(p+1)) { 657 | char c; 658 | 659 | p++; 660 | switch(*p) { 661 | case 'n': c = '\n'; break; 662 | case 'r': c = '\r'; break; 663 | case 't': c = '\t'; break; 664 | case 'b': c = '\b'; break; 665 | case 'a': c = '\a'; break; 666 | default: c = *p; break; 667 | } 668 | current = sdscatlen(current,&c,1); 669 | } else if (*p == '"') { 670 | /* closing quote must be followed by a space or 671 | * nothing at all. */ 672 | if (*(p+1) && !isspace(*(p+1))) goto err; 673 | done=1; 674 | } else if (!*p) { 675 | /* unterminated quotes */ 676 | goto err; 677 | } else { 678 | current = sdscatlen(current,p,1); 679 | } 680 | } else if (insq) { 681 | if (*p == '\\' && *(p+1) == '\'') { 682 | p++; 683 | current = sdscatlen(current,"'",1); 684 | } else if (*p == '\'') { 685 | /* closing quote must be followed by a space or 686 | * nothing at all. */ 687 | if (*(p+1) && !isspace(*(p+1))) goto err; 688 | done=1; 689 | } else if (!*p) { 690 | /* unterminated quotes */ 691 | goto err; 692 | } else { 693 | current = sdscatlen(current,p,1); 694 | } 695 | } else { 696 | switch(*p) { 697 | case ' ': 698 | case '\n': 699 | case '\r': 700 | case '\t': 701 | case '\0': 702 | done=1; 703 | break; 704 | case '"': 705 | inq=1; 706 | break; 707 | case '\'': 708 | insq=1; 709 | break; 710 | default: 711 | current = sdscatlen(current,p,1); 712 | break; 713 | } 714 | } 715 | if (*p) p++; 716 | } 717 | /* add the token to the vector */ 718 | vector = realloc(vector,((*argc)+1)*sizeof(char*)); 719 | vector[*argc] = current; 720 | (*argc)++; 721 | current = NULL; 722 | } else { 723 | /* Even on empty input string return something not NULL. */ 724 | if (vector == NULL) vector = malloc(sizeof(void*)); 725 | return vector; 726 | } 727 | } 728 | 729 | err: 730 | while((*argc)--) 731 | sdsfree(vector[*argc]); 732 | free(vector); 733 | if (current) sdsfree(current); 734 | *argc = 0; 735 | return NULL; 736 | } 737 | 738 | /* Modify the string substituting all the occurrences of the set of 739 | * characters specified in the 'from' string to the corresponding character 740 | * in the 'to' array. 741 | * 742 | * For instance: sdsmapchars(mystring, "ho", "01", 2) 743 | * will have the effect of turning the string "hello" into "0ell1". 744 | * 745 | * The function returns the sds string pointer, that is always the same 746 | * as the input pointer since no resize is needed. */ 747 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { 748 | size_t j, i, l = sdslen(s); 749 | 750 | for (j = 0; j < l; j++) { 751 | for (i = 0; i < setlen; i++) { 752 | if (s[j] == from[i]) { 753 | s[j] = to[i]; 754 | break; 755 | } 756 | } 757 | } 758 | return s; 759 | } 760 | 761 | /* Join an array of C strings using the specified separator (also a C string). 762 | * Returns the result as an sds string. */ 763 | sds sdsjoin(char **argv, int argc, char *sep) { 764 | sds join = sdsempty(); 765 | int j; 766 | 767 | for (j = 0; j < argc; j++) { 768 | join = sdscat(join, argv[j]); 769 | if (j != argc-1) join = sdscat(join,sep); 770 | } 771 | return join; 772 | } 773 | 774 | #ifdef SDS_TEST_MAIN 775 | #include 776 | #include "testhelp.h" 777 | 778 | int main(void) { 779 | { 780 | struct sdshdr *sh; 781 | sds x = sdsnew("foo"), y; 782 | 783 | test_cond("Create a string and obtain the length", 784 | sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) 785 | 786 | sdsfree(x); 787 | x = sdsnewlen("foo",2); 788 | test_cond("Create a string with specified length", 789 | sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) 790 | 791 | x = sdscat(x,"bar"); 792 | test_cond("Strings concatenation", 793 | sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); 794 | 795 | x = sdscpy(x,"a"); 796 | test_cond("sdscpy() against an originally longer string", 797 | sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) 798 | 799 | x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); 800 | test_cond("sdscpy() against an originally shorter string", 801 | sdslen(x) == 33 && 802 | memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) 803 | 804 | sdsfree(x); 805 | x = sdscatprintf(sdsempty(),"%d",123); 806 | test_cond("sdscatprintf() seems working in the base case", 807 | sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) 808 | 809 | sdsfree(x); 810 | x = sdstrim(sdsnew("xxciaoyyy"),"xy"); 811 | test_cond("sdstrim() correctly trims characters", 812 | sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) 813 | 814 | y = sdsrange(sdsdup(x),1,1); 815 | test_cond("sdsrange(...,1,1)", 816 | sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) 817 | 818 | sdsfree(y); 819 | y = sdsrange(sdsdup(x),1,-1); 820 | test_cond("sdsrange(...,1,-1)", 821 | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 822 | 823 | sdsfree(y); 824 | y = sdsrange(sdsdup(x),-2,-1); 825 | test_cond("sdsrange(...,-2,-1)", 826 | sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) 827 | 828 | sdsfree(y); 829 | y = sdsrange(sdsdup(x),2,1); 830 | test_cond("sdsrange(...,2,1)", 831 | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 832 | 833 | sdsfree(y); 834 | y = sdsrange(sdsdup(x),1,100); 835 | test_cond("sdsrange(...,1,100)", 836 | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 837 | 838 | sdsfree(y); 839 | y = sdsrange(sdsdup(x),100,100); 840 | test_cond("sdsrange(...,100,100)", 841 | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 842 | 843 | sdsfree(y); 844 | sdsfree(x); 845 | x = sdsnew("foo"); 846 | y = sdsnew("foa"); 847 | test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) 848 | 849 | sdsfree(y); 850 | sdsfree(x); 851 | x = sdsnew("bar"); 852 | y = sdsnew("bar"); 853 | test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) 854 | 855 | sdsfree(y); 856 | sdsfree(x); 857 | x = sdsnew("aar"); 858 | y = sdsnew("bar"); 859 | test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) 860 | 861 | { 862 | int oldfree; 863 | 864 | sdsfree(x); 865 | x = sdsnew("0"); 866 | sh = (void*) (x-(sizeof(struct sdshdr))); 867 | test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0); 868 | x = sdsMakeRoomFor(x,1); 869 | sh = (void*) (x-(sizeof(struct sdshdr))); 870 | test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0); 871 | oldfree = sh->free; 872 | x[1] = '1'; 873 | sdsIncrLen(x,1); 874 | test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1'); 875 | test_cond("sdsIncrLen() -- len", sh->len == 2); 876 | test_cond("sdsIncrLen() -- free", sh->free == oldfree-1); 877 | } 878 | } 879 | test_report() 880 | return 0; 881 | } 882 | #endif 883 | --------------------------------------------------------------------------------