├── .clang-format ├── .gitignore ├── Makefile ├── README ├── acme.c ├── acme.h ├── asyncio.c ├── asyncio.h ├── atomic.h ├── aws.c ├── aws.h ├── azure.c ├── azure.h ├── bytestream.h ├── cfg.c ├── cfg.h ├── cmd.c ├── cmd.h ├── cookie.c ├── cookie.h ├── ctrlsock.c ├── ctrlsock.h ├── curlhelpers.c ├── curlhelpers.h ├── db.c ├── db.h ├── dbl.c ├── dbl.h ├── dial.c ├── dial.h ├── dns.c ├── dns.h ├── err.c ├── err.h ├── filebundle.h ├── filebundle_disk.c ├── filebundle_embedded.c ├── fpipe.c ├── fpipe.h ├── gcp.c ├── gcp.h ├── gitver.mk ├── htsbuf.c ├── htsbuf.h ├── htsmsg.h ├── http.c ├── http.h ├── http_client.c ├── http_client.h ├── http_client_builtin.c ├── http_client_curl.c ├── http_parser.c ├── http_parser.h ├── init.h ├── intvec.c ├── intvec.h ├── irc.c ├── irc.h ├── json.c ├── json.h ├── libsvc.c ├── libsvc.h ├── libsvc.mk ├── mbuf.c ├── mbuf.h ├── memstream.c ├── memstream.h ├── misc.c ├── misc.h ├── mkbundle ├── murmur3.c ├── murmur3.h ├── ntv.c ├── ntv.h ├── ntv_binary.c ├── ntv_cbor.c ├── ntv_json.c ├── ntv_msgpack.c ├── ntv_xml.c ├── queue.h ├── redblack.h ├── skeleton ├── Makefile ├── mk │ ├── Darwin.mk │ ├── Linux.mk │ └── asan.mk └── src │ └── main.c ├── sock.c ├── sock.h ├── sources.mk ├── stream.c ├── stream.h ├── strtab.h ├── strvec.c ├── strvec.h ├── talloc.c ├── talloc.h ├── task.c ├── task.h ├── tbm.c ├── tbm.h ├── tcp.c ├── tcp.h ├── tcp_server.c ├── threading.h ├── trace.c ├── trace.h ├── trap.c ├── trap.h ├── utf8.c ├── utf8.h ├── vec.h ├── websocket.c ├── websocket.h ├── websocket_client.c └── websocket_client.h /.clang-format: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andoma/libsvc/30e9b790016bf64c753f53a151e48cc2853a8395/.clang-format -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.d 3 | *.o 4 | libsvc.so 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile to build shared library of libsvc 3 | # 4 | 5 | MAJOR_VERSION := 0 6 | MINOR_VERSION := 1 7 | PATCH_VERSION := 0 8 | 9 | FULL_VERSION := ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION} 10 | 11 | prefix ?= /usr/local 12 | 13 | WITH_CURL ?= yes 14 | WITH_HTTP_SERVER ?= yes 15 | WITH_WS_SERVER ?= yes 16 | WITH_WS_CLIENT ?= yes 17 | 18 | include sources.mk 19 | 20 | OBJS= $(libsvc_SRCS:%.c=%.o) 21 | DEPS= ${OBJS:%.o=%.d} 22 | 23 | CFLAGS += -Wall -Werror -fPIC -O2 -g 24 | LIB = libsvc.so 25 | 26 | ${LIB}: ${OBJS} Makefile sources.mk 27 | ${CC} -shared -o ${LIB} ${OBJS} 28 | 29 | %.o: %.c Makefile sources.mk 30 | ${CC} -MD -MP ${CFLAGS} -c -o $@ $< 31 | 32 | clean: 33 | rm -f ${LIB} *~ *.o *.d 34 | 35 | install: 36 | mkdir -p $(DESTDIR)$(prefix)/lib 37 | install -T ${LIB} $(DESTDIR)$(prefix)/lib/${LIB}.${FULL_VERSION} 38 | ln -sf ${LIB}.${FULL_VERSION} $(DESTDIR)$(prefix)/lib/${LIB}.${MAJOR_VERSION} 39 | ln -sf ${LIB}.${MAJOR_VERSION} $(DESTDIR)$(prefix)/lib/${LIB} 40 | 41 | mkdir -p $(DESTDIR)$(prefix)/include/libsvc 42 | 43 | for file in ${libsvc_INCS}; do \ 44 | install $${file} $(DESTDIR)$(prefix)/include/libsvc ; \ 45 | done 46 | 47 | -include $(DEPS) 48 | 49 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a library that helps building unix services. 2 | 3 | The library is licensed under the MIT license. 4 | 5 | 6 | -------------------------------------------------------------------------------- /acme.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | 5 | typedef struct acme_callbacks { 6 | 7 | char *(*account_key_get)(void *opaque); 8 | 9 | int (*account_key_set)(void *opaque, const char *pem); 10 | 11 | int (*present_http_01)(void *opaque, const char *domain, 12 | const char *token, const char *payload); 13 | 14 | int (*present_dns_01)(void *opaque, const char *domain, 15 | const char *payload); 16 | 17 | char *(*load_cert)(void *opaque); 18 | 19 | int (*save_cert)(void *opaque, const char *json); 20 | 21 | } acme_callbacks_t; 22 | 23 | struct ntv; 24 | struct strvec; 25 | 26 | ntv_t *acme_acquire_cert(const acme_callbacks_t *callbacks, void *opaque, 27 | const strvec_t *domains, const char *contact, 28 | const char *directory_url, int keysize); 29 | -------------------------------------------------------------------------------- /asyncio.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include "mbuf.h" 28 | #include "atomic.h" 29 | 30 | /************************************************************************** 31 | * Timers 32 | **************************************************************************/ 33 | 34 | 35 | typedef struct asyncio_timer { 36 | LIST_ENTRY(asyncio_timer) at_link; 37 | int64_t at_expire; 38 | void (*at_fn)(void *opaque, int64_t now); 39 | void *at_opaque; 40 | } asyncio_timer_t; 41 | 42 | void asyncio_timer_init(asyncio_timer_t *at, void (*fn)(void *opaque, 43 | int64_t now), 44 | void *opaque); 45 | 46 | void asyncio_timer_arm_delta(asyncio_timer_t *at, uint64_t delta); 47 | 48 | void asyncio_timer_disarm(asyncio_timer_t *at); 49 | 50 | int64_t asyncio_now(void); 51 | 52 | int64_t asyncio_get_monotime(void); 53 | 54 | /************************************************************************** 55 | * IO 56 | **************************************************************************/ 57 | 58 | #define ASYNCIO_FLAG_THREAD_SAFE 0x1 59 | #define ASYNCIO_FLAG_SSL_VERIFY_CERT 0x2 60 | #define ASYNCIO_FLAG_NO_DELAY 0x4 61 | 62 | typedef struct asyncio_sslctx asyncio_sslctx_t; 63 | 64 | typedef struct asyncio_fd asyncio_fd_t; 65 | 66 | void asyncio_init(void); 67 | 68 | typedef int (asyncio_accept_cb_t)(void *opaque, int fd, 69 | struct sockaddr *peer, 70 | struct sockaddr *self); 71 | 72 | typedef void (asyncio_error_cb_t)(void *opaque, int error); 73 | 74 | typedef void (asyncio_read_cb_t)(void *opaque, struct mbuf *hq); 75 | 76 | typedef void (asyncio_poll_cb_t)(struct asyncio_fd *, void *opaque); 77 | 78 | typedef void (asyncio_socket_trace_cb_t)(void *opaque, const char *str); 79 | 80 | asyncio_fd_t *asyncio_bind(const char *bindaddr, 81 | int port, 82 | asyncio_accept_cb_t *cb, 83 | void *opaque, int flags); 84 | 85 | asyncio_fd_t *asyncio_dgram(int fd, asyncio_poll_cb_t *input, 86 | void *opaque); 87 | 88 | asyncio_fd_t *asyncio_connect(int fd, asyncio_error_cb_t *cb, void *opaque); 89 | 90 | asyncio_fd_t *asyncio_stream(int fd, 91 | asyncio_read_cb_t *read, 92 | asyncio_error_cb_t *err, 93 | void *opaque, 94 | int flags, 95 | asyncio_sslctx_t *sslctx, 96 | const char *hostname, 97 | const char *title, 98 | asyncio_socket_trace_cb_t *trace); 99 | 100 | int asyncio_detach(asyncio_fd_t *af); 101 | 102 | void asyncio_close(asyncio_fd_t *af); 103 | 104 | int asyncio_send(asyncio_fd_t *af, const void *buf, size_t len, int cork); 105 | 106 | int asyncio_send_with_hdr(asyncio_fd_t *af, 107 | const void *hdr_buf, size_t hdr_len, 108 | const void *buf, size_t len, 109 | int cork, int queue_index); 110 | 111 | int asyncio_sendq(asyncio_fd_t *af, mbuf_t *hq, int cork, int queue_index); 112 | 113 | int asyncio_sendq_with_hdr(asyncio_fd_t *af, const void *hdr_buf, 114 | size_t hdr_len, mbuf_t *q, 115 | int cork, int queue_index); 116 | 117 | void asyncio_send_lock(asyncio_fd_t *af); 118 | 119 | void asyncio_send_unlock(asyncio_fd_t *af); 120 | 121 | int asyncio_sendq_with_hdr_locked(asyncio_fd_t *af, const void *hdr_buf, 122 | size_t hdr_len, mbuf_t *q, 123 | int cork, int queue_index); 124 | 125 | void asyncio_process_pending(asyncio_fd_t *fd); 126 | 127 | void asyncio_shutdown(asyncio_fd_t *fd); 128 | 129 | void asyncio_fd_retain(asyncio_fd_t *af); 130 | 131 | void asyncio_fd_release(asyncio_fd_t *af); 132 | 133 | int asyncio_wait_send_buffer(asyncio_fd_t *af, int size); 134 | 135 | size_t asyncio_fd_get_queue_length(asyncio_fd_t *af, int queue_index); 136 | 137 | const char *asyncio_fd_get_sni_name(asyncio_fd_t *af); 138 | 139 | int asyncio_get_fd(asyncio_fd_t *af); 140 | 141 | /************************************************************************* 142 | * Workers 143 | *************************************************************************/ 144 | 145 | int asyncio_add_worker(void (*fn)(void)); 146 | 147 | void asyncio_wakeup_worker(int id); 148 | 149 | void asyncio_run_task(void (*fn)(void *aux), void *aux); 150 | 151 | void asyncio_run_task_blocking(void (*fn)(void *aux), void *aux); 152 | 153 | /************************************************************************ 154 | * SSL / TLS 155 | ************************************************************************/ 156 | 157 | asyncio_sslctx_t *asyncio_sslctx_server_from_files(const char *priv_key_file, 158 | const char *cert_file); 159 | 160 | asyncio_sslctx_t *asyncio_sslctx_server_from_pem(const char *priv_key_pem, 161 | const char *cert_pem); 162 | 163 | typedef struct { 164 | const char *hostname; 165 | const char *priv_key_pem; 166 | const char *cert_pem; 167 | int is_wildcard; 168 | } asyncio_sslhost_t; 169 | 170 | asyncio_sslctx_t *asyncio_sslctx_server_hosts(const asyncio_sslhost_t *hosts, 171 | size_t num_hosts); 172 | 173 | asyncio_sslctx_t *asyncio_sslctx_client(void); 174 | 175 | void asyncio_sslctx_free(asyncio_sslctx_t *ctx); 176 | -------------------------------------------------------------------------------- /atomic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if (__GNUC__ >= 4 && __GNUC_MINOR__ >=3) || (__GNUC__ >= 5) || defined(__APPLE__) || defined(__native_client__) 4 | 5 | typedef struct atomic { 6 | int v; 7 | } atomic_t; 8 | 9 | 10 | static inline void 11 | atomic_inc(atomic_t *a) 12 | { 13 | __sync_add_and_fetch(&a->v, 1); 14 | } 15 | 16 | static inline void 17 | atomic_add(atomic_t *a, int v) 18 | { 19 | __sync_add_and_fetch(&a->v, v); 20 | } 21 | 22 | static inline int __attribute__((warn_unused_result)) 23 | atomic_add_and_fetch(atomic_t *a, int v) 24 | { 25 | return __sync_add_and_fetch(&a->v, v); 26 | } 27 | 28 | static inline int 29 | atomic_dec(atomic_t *a) 30 | { 31 | return __sync_add_and_fetch(&a->v, -1); 32 | } 33 | 34 | static inline int 35 | atomic_get(const atomic_t *a) 36 | { 37 | return __atomic_load_n(&a->v, __ATOMIC_SEQ_CST); 38 | } 39 | 40 | static inline void 41 | atomic_set(atomic_t *a, int v) 42 | { 43 | __atomic_store_n(&a->v, v, __ATOMIC_SEQ_CST); 44 | } 45 | 46 | static inline int 47 | atomic_get_and_set(atomic_t *a, int v) 48 | { 49 | return __sync_lock_test_and_set(&a->v, v); 50 | } 51 | 52 | 53 | static inline void 54 | atomic_max(atomic_t *a, int v) 55 | { 56 | while(1) { 57 | const int current = atomic_get(a); 58 | const int newval = v > current ? v : current; 59 | if(__sync_bool_compare_and_swap(&a->v, current, newval)) 60 | break; 61 | } 62 | } 63 | 64 | 65 | 66 | #else 67 | #error Missing atomic ops 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /aws.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct ntv; 7 | 8 | 9 | typedef struct { 10 | const char *id; 11 | const char *secret; 12 | const char *token; 13 | } aws_creds_t; 14 | 15 | aws_creds_t aws_get_creds(void); 16 | 17 | aws_creds_t aws_get_creds_or_fail(void); 18 | 19 | char *aws_sig4_canonical_request_hash(const char *http_method, 20 | const char *canonical_uri, 21 | const struct ntv *query_args, 22 | const struct ntv *headers, 23 | const char *hashed_payload); 24 | 25 | char *aws_sig4_gen_signature(const char *http_method, 26 | const char *uri, 27 | const struct ntv *query_args, 28 | const struct ntv *headers, 29 | const char *payload_hash, 30 | time_t timestamp, 31 | aws_creds_t creds, 32 | const char *service, 33 | const char *region); 34 | 35 | char *aws_sig4_gen_auth_header(const char *http_method, 36 | const char *uri, 37 | const struct ntv *query_args, 38 | const struct ntv *headers, 39 | const char *payload_hash, 40 | time_t timestamp, 41 | aws_creds_t creds, 42 | const char *service, 43 | const char *region); 44 | 45 | char *aws_SHA256_hex(const void *data, size_t len); 46 | 47 | char *aws_isodate(time_t timestamp); 48 | 49 | 50 | char *aws_s3_make_url(const char *method, 51 | const char *region, 52 | const char *bucket, 53 | const char *path, 54 | aws_creds_t creds, 55 | const struct ntv *extra_query_args); 56 | 57 | struct ntv *aws_invoke(const char *region, 58 | const char *service, 59 | const char *target, 60 | aws_creds_t creds, 61 | struct ntv *req); 62 | 63 | const char *aws_invoked_transient_error(const struct ntv *response); 64 | 65 | const char *aws_invoked_error(const struct ntv *response); 66 | 67 | const struct ntv *aws_invoked_result(const struct ntv *response); 68 | 69 | void aws_test(void); 70 | -------------------------------------------------------------------------------- /azure.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "azure.h" 6 | #include "misc.h" 7 | #include "trace.h" 8 | #include "ntv.h" 9 | #include "http.h" 10 | #include "http_client.h" 11 | 12 | #include 13 | 14 | char * 15 | azure_sas_token(const char *resource, const char *sakey, 16 | int valid_duration, const char *keyname) 17 | { 18 | scoped_char *canonical_resource = 19 | url_escape_alloc(resource, URL_ESCAPE_PARAM); 20 | 21 | time_t ttl = time(NULL) + valid_duration; 22 | scoped_char *to_sign = fmt("%s\n%ld", canonical_resource, (long)ttl); 23 | 24 | uint8_t key[256]; 25 | int keylen = base64_decode(key, sakey, sizeof(key)); 26 | 27 | uint8_t hmac[32]; 28 | 29 | HMAC(EVP_sha256(), key, keylen, (const uint8_t *)to_sign, 30 | strlen(to_sign), hmac, NULL); 31 | 32 | scoped_char *b64_hmac = base64_encode_a(hmac, sizeof(hmac), 33 | BASE64_STANDARD); 34 | 35 | scoped_char *sig = url_escape_alloc(b64_hmac, URL_ESCAPE_PARAM); 36 | 37 | return fmt("SharedAccessSignature sr=%s&sig=%s&se=%ld%s%s", 38 | canonical_resource, sig, (long)ttl, 39 | keyname ? "&skn=" : "", 40 | keyname ?: ""); 41 | } 42 | 43 | 44 | ntv_t * 45 | azure_vm_get_machine_identity(void) 46 | { 47 | char errbuf[512]; 48 | const char *url = "http://169.254.169.254/metadata/instance?api-version=2018-02-01"; 49 | 50 | scoped_http_result(hcr); 51 | 52 | if(http_client_request(&hcr, url, 53 | HCR_TIMEOUT(2), 54 | HCR_FLAGS(HCR_DECODE_BODY_AS_JSON), 55 | HCR_ERRBUF(errbuf, sizeof(errbuf)), 56 | HCR_HEADER("Metadata", "true"), 57 | NULL)) { 58 | trace(LOG_ERR, "Failed to get azure instance metadata from %s -- %s", 59 | url, errbuf); 60 | return NULL; 61 | } 62 | 63 | ntv_t *result = hcr.hcr_json_result; 64 | hcr.hcr_json_result = NULL; 65 | return result; 66 | } 67 | 68 | 69 | ntv_t * 70 | azure_vm_get_machine_token(const char *aud) 71 | { 72 | char errbuf[512]; 73 | scoped_char *url = fmt("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=%s", aud); 74 | 75 | scoped_http_result(hcr); 76 | 77 | if(http_client_request(&hcr, url, 78 | HCR_TIMEOUT(2), 79 | HCR_FLAGS(HCR_DECODE_BODY_AS_JSON), 80 | HCR_ERRBUF(errbuf, sizeof(errbuf)), 81 | HCR_HEADER("Metadata", "true"), 82 | NULL)) { 83 | trace(LOG_ERR, "Failed to get azure instance token for %s from %s -- %s", 84 | aud, url, errbuf); 85 | return NULL; 86 | } 87 | 88 | 89 | ntv_t *result = hcr.hcr_json_result; 90 | hcr.hcr_json_result = NULL; 91 | return result; 92 | } 93 | -------------------------------------------------------------------------------- /azure.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | char *azure_sas_token(const char *resource, const char *sakey, 4 | int valid_duration, const char *keyname); 5 | 6 | struct ntv *azure_vm_get_machine_identity(void); 7 | 8 | struct ntv *azure_vm_get_machine_token(const char *aud); 9 | -------------------------------------------------------------------------------- /bytestream.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | #include 26 | #include 27 | 28 | static __inline void wr64_be(uint8_t *ptr, uint64_t val) 29 | { 30 | #if !defined(__BIG_ENDIAN__) 31 | val = __builtin_bswap64(val); 32 | #endif 33 | memcpy(ptr, &val, 8); 34 | } 35 | 36 | 37 | static __inline void wr32_be(uint8_t *ptr, uint32_t val) 38 | { 39 | #if !defined(__BIG_ENDIAN__) 40 | val = __builtin_bswap32(val); 41 | #endif 42 | memcpy(ptr, &val, 4); 43 | } 44 | 45 | 46 | static __inline void wr16_be(uint8_t *ptr, uint16_t val) 47 | { 48 | #if !defined(__BIG_ENDIAN__) 49 | val = ((val >> 8) & 0xff) | ((val << 8) & 0xff00); 50 | #endif 51 | memcpy(ptr, &val, 2); 52 | } 53 | 54 | 55 | 56 | static __inline uint64_t rd64_be(const uint8_t *ptr) 57 | { 58 | uint64_t val; 59 | memcpy(&val, ptr, 8); 60 | #if !defined(__BIG_ENDIAN__) 61 | val = __builtin_bswap64(val); 62 | #endif 63 | return val; 64 | } 65 | 66 | 67 | static __inline uint32_t rd32_be(const uint8_t *ptr) 68 | { 69 | uint32_t val; 70 | memcpy(&val, ptr, 4); 71 | #if !defined(__BIG_ENDIAN__) 72 | val = __builtin_bswap32(val); 73 | #endif 74 | return val; 75 | } 76 | 77 | 78 | 79 | static __inline uint16_t rd16_be(const uint8_t *ptr) 80 | { 81 | uint16_t val; 82 | memcpy(&val, ptr, 2); 83 | #if !defined(__BIG_ENDIAN__) 84 | val = ((val >> 8) & 0xff) | ((val << 8) & 0xff00); 85 | #endif 86 | return val; 87 | } 88 | 89 | 90 | 91 | 92 | static __inline void wr64_le(uint8_t *ptr, uint64_t val) 93 | { 94 | #if defined(__BIG_ENDIAN__) 95 | val = __builtin_bswap64(val); 96 | #endif 97 | memcpy(ptr, &val, 8); 98 | } 99 | 100 | 101 | static __inline void wr32_le(uint8_t *ptr, uint32_t val) 102 | { 103 | #if defined(__BIG_ENDIAN__) 104 | val = __builtin_bswap32(val); 105 | #endif 106 | memcpy(ptr, &val, 4); 107 | } 108 | 109 | 110 | static __inline void wr16_le(uint8_t *ptr, uint16_t val) 111 | { 112 | #if defined(__BIG_ENDIAN__) 113 | val = ((val >> 8) & 0xff) | ((val << 8) & 0xff00); 114 | #endif 115 | memcpy(ptr, &val, 2); 116 | } 117 | 118 | 119 | 120 | static __inline uint64_t rd64_le(const uint8_t *ptr) 121 | { 122 | uint64_t val; 123 | memcpy(&val, ptr, 8); 124 | #if defined(__BIG_ENDIAN__) 125 | val = __builtin_bswap64(val); 126 | #endif 127 | return val; 128 | } 129 | 130 | 131 | static __inline uint32_t rd32_le(const uint8_t *ptr) 132 | { 133 | uint32_t val; 134 | memcpy(&val, ptr, 4); 135 | #if defined(__BIG_ENDIAN__) 136 | val = __builtin_bswap32(val); 137 | #endif 138 | return val; 139 | } 140 | 141 | 142 | 143 | static __inline uint16_t rd16_le(const uint8_t *ptr) 144 | { 145 | uint16_t val; 146 | memcpy(&val, ptr, 2); 147 | #if defined(__BIG_ENDIAN__) 148 | val = ((val >> 8) & 0xff) | ((val << 8) & 0xff00); 149 | #endif 150 | return val; 151 | } 152 | -------------------------------------------------------------------------------- /cfg.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "misc.h" 33 | #include "trace.h" 34 | #include "ntv.h" 35 | #include "cfg.h" 36 | #include "cmd.h" 37 | 38 | LIST_HEAD(reload_cb_list, reload_cb); 39 | static struct reload_cb_list reload_cbs; 40 | static pthread_mutex_t cfg_mutex = PTHREAD_MUTEX_INITIALIZER; 41 | static pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER; 42 | static cfg_t *cfgroot; 43 | 44 | 45 | typedef struct reload_cb { 46 | void (*fn)(void); 47 | LIST_ENTRY(reload_cb) link; 48 | } reload_cb_t; 49 | 50 | /** 51 | * 52 | */ 53 | void 54 | cfg_add_reload_cb(void (*fn)(void)) 55 | { 56 | reload_cb_t *rc = malloc(sizeof(reload_cb_t)); 57 | rc->fn = fn; 58 | pthread_mutex_lock(&reload_mutex); 59 | LIST_INSERT_HEAD(&reload_cbs, rc, link); 60 | pthread_mutex_unlock(&reload_mutex); 61 | } 62 | 63 | /** 64 | * 65 | */ 66 | static void 67 | cfg_call_reload_callbacks(void) 68 | { 69 | reload_cb_t *rc; 70 | pthread_mutex_lock(&reload_mutex); 71 | LIST_FOREACH(rc, &reload_cbs, link) 72 | rc->fn(); 73 | pthread_mutex_unlock(&reload_mutex); 74 | } 75 | 76 | 77 | /** 78 | * 79 | */ 80 | cfg_t * 81 | cfg_get_root(void) 82 | { 83 | pthread_mutex_lock(&cfg_mutex); 84 | cfg_t *c = ntv_retain(cfgroot); 85 | pthread_mutex_unlock(&cfg_mutex); 86 | return c; 87 | } 88 | 89 | void 90 | cfg_releasep(cfg_t **p) 91 | { 92 | if(*p) { 93 | pthread_mutex_lock(&cfg_mutex); 94 | ntv_release(*p); 95 | pthread_mutex_unlock(&cfg_mutex); 96 | } 97 | } 98 | 99 | 100 | int 101 | cfg_load_str(const char *json, char *errbuf, size_t errlen) 102 | { 103 | ntv_t *msg = ntv_json_deserialize(json, errbuf, errlen); 104 | if(msg == NULL) 105 | return -1; 106 | pthread_mutex_lock(&cfg_mutex); 107 | if(cfgroot != NULL) 108 | ntv_release(cfgroot); 109 | 110 | cfgroot = msg; 111 | pthread_mutex_unlock(&cfg_mutex); 112 | trace(LOG_NOTICE, "Config updated"); 113 | cfg_call_reload_callbacks(); 114 | return 0; 115 | } 116 | 117 | /** 118 | * 119 | */ 120 | int 121 | cfg_load(const char *filename, char *errbuf, size_t errlen) 122 | { 123 | static char *lastfilename; 124 | 125 | pthread_mutex_lock(&cfg_mutex); 126 | 127 | if(filename == NULL) { 128 | if(lastfilename == NULL) { 129 | snprintf(errbuf, errlen, "No path for config"); 130 | trace(LOG_ERR, "No path for config"); 131 | pthread_mutex_unlock(&cfg_mutex); 132 | return -1; 133 | } 134 | filename = mystrdupa(lastfilename); 135 | } else { 136 | free(lastfilename); 137 | lastfilename = strdup(filename); 138 | } 139 | 140 | pthread_mutex_unlock(&cfg_mutex); 141 | 142 | trace(LOG_NOTICE, "About to load config form %s", filename); 143 | 144 | scoped_char *cfgtxt = readfile(filename, NULL); 145 | if(cfgtxt == NULL) { 146 | const char *errstr = strerror(errno); 147 | snprintf(errbuf, errlen, "Unable to read file %s -- %s", filename, errstr); 148 | trace(LOG_ERR, "Unable to read file %s -- %s", filename, errstr); 149 | trace(LOG_ERR, "Config not updated"); 150 | return -1; 151 | } 152 | 153 | char errbuf2[256]; 154 | int r = cfg_load_str(cfgtxt, errbuf2, sizeof(errbuf2)); 155 | if(r) { 156 | snprintf(errbuf, errlen, "Unable to parse file %s -- %s", filename, errbuf2); 157 | trace(LOG_ERR, "Unable to parse file %s -- %s", filename, errbuf2); 158 | trace(LOG_ERR, "Config not updated"); 159 | return -1; 160 | } 161 | return 0; 162 | } 163 | 164 | 165 | 166 | /** 167 | * 168 | */ 169 | const char * 170 | cfg_get_str(const ntv_t *msg, const char **vec, const char *def) 171 | { 172 | return ntv_get_str(ntv_field_from_path(msg, vec), NULL) ?: def; 173 | } 174 | 175 | 176 | /** 177 | * 178 | */ 179 | int64_t 180 | cfg_get_s64(const ntv_t *msg, const char **path, int64_t def) 181 | { 182 | return ntv_get_int64(ntv_field_from_path(msg, path), NULL, def); 183 | } 184 | 185 | 186 | /** 187 | * 188 | */ 189 | double 190 | cfg_get_dbl(const ntv_t *msg, const char **path, double def) 191 | { 192 | return ntv_get_double(ntv_field_from_path(msg, path), NULL, def); 193 | } 194 | 195 | 196 | /** 197 | * 198 | */ 199 | int 200 | cfg_get_int(const ntv_t *msg, const char **path, int def) 201 | { 202 | return ntv_get_int(ntv_field_from_path(msg, path), NULL, def); 203 | } 204 | 205 | #if 0 206 | /** 207 | * 208 | */ 209 | cfg_t * 210 | cfg_find_map(cfg_t *c, const char *key, const char *value) 211 | { 212 | if(c == NULL) 213 | return NULL; 214 | 215 | htsmsg_field_t *f; 216 | HTSMSG_FOREACH(f, c) { 217 | htsmsg_t *m = htsmsg_get_map_by_field(f); 218 | if(m == NULL) 219 | continue; 220 | 221 | const char *s = htsmsg_get_str(m, key); 222 | if(s != NULL && !strcmp(value, s)) 223 | return m; 224 | } 225 | return NULL; 226 | } 227 | #endif 228 | 229 | 230 | static int 231 | reload_configuration(const char *user, 232 | int argc, const char **argv, int *intv, 233 | void (*msg)(void *opaque, const char *fmt, ...), 234 | void *opaque) 235 | { 236 | char errbuf[512]; 237 | if(cfg_load(NULL, errbuf, sizeof(errbuf))) { 238 | msg(opaque, "Unable to load configuration -- %s", errbuf); 239 | return 1; 240 | } else { 241 | msg(opaque, "Config reloaded OK"); 242 | return 0; 243 | } 244 | } 245 | 246 | CMD(reload_configuration, 247 | CMD_LITERAL("reload"), 248 | CMD_LITERAL("configuration")); 249 | -------------------------------------------------------------------------------- /cfg.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | 25 | #pragma once 26 | #include "ntv.h" 27 | 28 | typedef ntv_t cfg_t; 29 | 30 | int cfg_load(const char *filename, char *errbuf, size_t errlen); 31 | 32 | int cfg_load_str(const char *json, char *errbuf, size_t errlen); 33 | 34 | cfg_t *cfg_get_root(void); 35 | 36 | void cfg_releasep(cfg_t **p); 37 | 38 | #define cfg_root(x) cfg_t *x __attribute__((cleanup(cfg_releasep))) = cfg_get_root(); 39 | 40 | #define CFG(name...) (const char *[]){name, NULL} 41 | #define CFGI(x) (const char *[]){HTSMSG_INDEX(x), NULL} 42 | #define CFG_INDEX(x) NTV_INDEX(x) 43 | 44 | const char *cfg_get_str(const cfg_t *c, const char **path, const char *def); 45 | 46 | int64_t cfg_get_s64(const cfg_t *c, const char **path, int64_t def); 47 | 48 | int cfg_get_int(const cfg_t *c, const char **path, int def); 49 | 50 | double cfg_get_dbl(const cfg_t *c, const char **path, double def); 51 | 52 | #define cfg_get_map ntv_get_map 53 | 54 | #define cfg_get_list ntv_get_list 55 | 56 | #define cfg_list_length ntv_num_children 57 | 58 | void cfg_add_reload_cb(void (*fn)(void)); 59 | -------------------------------------------------------------------------------- /cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "strvec.h" 4 | 5 | int cmd_exec(const char *line, const char *user, 6 | void (*msg)(void *opaque, const char *fmt, ...), 7 | void *opaque); 8 | 9 | int cmd_complete(const char *line, const char *user, 10 | void (*msg)(void *opaque, const char *fmt, ...), 11 | void *opaque); 12 | 13 | int cmd_complete2(const char *line, const char *user, 14 | void (*msg)(void *opaque, const char *fmt, ...), 15 | void *opaque); 16 | 17 | #define CMD_TOKEN_LITERAL 1 18 | #define CMD_TOKEN_VARSTR 2 19 | #define CMD_TOKEN_ROL 3 // Rest of line 20 | #define CMD_TOKEN_OPTSTR 4 21 | 22 | typedef strvec_t(cmd_list_options_t)(const char *user); 23 | 24 | typedef struct cmd_token { 25 | int type; 26 | const char *str; 27 | cmd_list_options_t *lister; 28 | } cmd_token_t; 29 | 30 | typedef int (cmd_invoke_t)(const char *user, 31 | int argc, const char **argv, int *intv, 32 | void (*msg)(void *opaque, const char *fmt, ...), 33 | void *opaque); 34 | 35 | #define CMD_LITERAL(s) { \ 36 | .type = CMD_TOKEN_LITERAL, \ 37 | .str = s } 38 | 39 | #define CMD_VARSTR(s) { \ 40 | .type = CMD_TOKEN_VARSTR, \ 41 | .str = s } 42 | 43 | #define CMD_ROL(s) { \ 44 | .type = CMD_TOKEN_ROL, \ 45 | .str = s } 46 | 47 | #define CMD_OPTSTR(l) { \ 48 | .type = CMD_TOKEN_OPTSTR, \ 49 | .lister = l } 50 | 51 | typedef struct cmd { 52 | cmd_invoke_t *invoker; 53 | const cmd_token_t pattern[]; 54 | } cmd_t; 55 | 56 | void cmd_register(const cmd_t *cmd); 57 | 58 | #define CMD_COMBINE1(X,Y) X##Y 59 | #define CMD_COMBINE(X,Y) CMD_COMBINE1(X,Y) 60 | 61 | #define CMD(invoke, x...) \ 62 | static const cmd_t CMD_COMBINE(CLICMD_, __LINE__) = {.invoker = invoke, .pattern = {x, {.type = 0}}}; \ 63 | static void __attribute__((constructor)) CMD_COMBINE(cmd_do_register_, __LINE__)(void) { \ 64 | cmd_register(&CMD_COMBINE(CLICMD_, __LINE__)); \ 65 | } 66 | 67 | void cmd_dump_tree(void); 68 | -------------------------------------------------------------------------------- /cookie.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mbuf.h" 6 | 7 | #include "cookie.h" 8 | #include "misc.h" 9 | 10 | #define COOKIE_KEY_LEN 16 11 | #define COOKIE_NONCE_LEN 12 12 | #define COOKIE_TAG_LEN 16 13 | 14 | 15 | struct cookie_engine { 16 | 17 | EVP_CIPHER_CTX *enc; 18 | EVP_CIPHER_CTX *dec; 19 | 20 | pthread_mutex_t enc_mutex; 21 | 22 | pthread_mutex_t dec_mutex; 23 | }; 24 | 25 | 26 | 27 | cookie_engine_t * 28 | cookie_engine_create(const char *secret, const char *salt, int pbkdf2_rounds) 29 | { 30 | uint8_t key[COOKIE_KEY_LEN]; 31 | 32 | if(!PKCS5_PBKDF2_HMAC(secret, strlen(secret), 33 | (const uint8_t *)salt, salt ? strlen(salt) : 0, 34 | pbkdf2_rounds, 35 | EVP_sha256(), sizeof(key), key)) { 36 | return NULL; 37 | } 38 | 39 | cookie_engine_t *ce = calloc(1, sizeof(cookie_engine_t)); 40 | 41 | ce->enc = EVP_CIPHER_CTX_new(); 42 | EVP_EncryptInit_ex(ce->enc, EVP_aes_128_gcm(), NULL, NULL, NULL); 43 | EVP_CIPHER_CTX_ctrl(ce->enc, EVP_CTRL_GCM_SET_IVLEN, COOKIE_NONCE_LEN, NULL); 44 | EVP_CIPHER_CTX_ctrl(ce->enc, EVP_CTRL_GCM_SET_TAG, COOKIE_TAG_LEN, NULL); 45 | EVP_EncryptInit_ex(ce->enc, NULL, NULL, key, NULL); 46 | 47 | ce->dec = EVP_CIPHER_CTX_new(); 48 | EVP_EncryptInit_ex(ce->dec, EVP_aes_128_gcm(), NULL, NULL, NULL); 49 | EVP_CIPHER_CTX_ctrl(ce->dec, EVP_CTRL_GCM_SET_IVLEN, COOKIE_NONCE_LEN, NULL); 50 | EVP_CIPHER_CTX_ctrl(ce->dec, EVP_CTRL_GCM_SET_TAG, COOKIE_TAG_LEN, NULL); 51 | EVP_EncryptInit_ex(ce->dec, NULL, NULL, key, NULL); 52 | 53 | pthread_mutex_init(&ce->enc_mutex, NULL); 54 | pthread_mutex_init(&ce->dec_mutex, NULL); 55 | return ce; 56 | } 57 | 58 | void 59 | cookie_engine_destroy(cookie_engine_t *ce) 60 | { 61 | pthread_mutex_destroy(&ce->enc_mutex); 62 | pthread_mutex_destroy(&ce->dec_mutex); 63 | EVP_CIPHER_CTX_free(ce->enc); 64 | EVP_CIPHER_CTX_free(ce->dec); 65 | free(ce); 66 | } 67 | 68 | 69 | char * 70 | cookie_encode(cookie_engine_t *ce, const ntv_t *msg) 71 | { 72 | if(ce == NULL || msg == NULL) 73 | return NULL; 74 | 75 | scoped_mbuf_t msg_plaintext = MBUF_INITIALIZER(msg_plaintext); 76 | ntv_binary_serialize(msg, &msg_plaintext); 77 | 78 | const void *pt = mbuf_pullup(&msg_plaintext, msg_plaintext.mq_size); 79 | 80 | const size_t encrypted_len = 81 | COOKIE_NONCE_LEN + COOKIE_TAG_LEN + msg_plaintext.mq_size; 82 | 83 | uint8_t *encrypted = alloca(encrypted_len); 84 | get_random_bytes(encrypted, COOKIE_NONCE_LEN); 85 | 86 | pthread_mutex_lock(&ce->enc_mutex); 87 | if(EVP_EncryptInit_ex(ce->enc, NULL, NULL, NULL, encrypted) != 1) { 88 | pthread_mutex_unlock(&ce->enc_mutex); 89 | return NULL; 90 | } 91 | 92 | int outlen = 0; 93 | if(EVP_EncryptUpdate(ce->enc, encrypted + COOKIE_NONCE_LEN + COOKIE_TAG_LEN, 94 | &outlen, pt, msg_plaintext.mq_size) != 1) { 95 | pthread_mutex_unlock(&ce->enc_mutex); 96 | return NULL; 97 | } 98 | 99 | int tmplen = 0; 100 | if(EVP_EncryptFinal_ex(ce->enc, 101 | encrypted + COOKIE_NONCE_LEN + COOKIE_TAG_LEN, 102 | &tmplen) != 1) { 103 | pthread_mutex_unlock(&ce->enc_mutex); 104 | return NULL; 105 | } 106 | 107 | if(EVP_CIPHER_CTX_ctrl(ce->enc, EVP_CTRL_CCM_GET_TAG, COOKIE_TAG_LEN, 108 | encrypted + COOKIE_NONCE_LEN) != 1) { 109 | pthread_mutex_unlock(&ce->enc_mutex); 110 | return NULL; 111 | } 112 | 113 | pthread_mutex_unlock(&ce->enc_mutex); 114 | return base64_encode_a(encrypted, encrypted_len, BASE64_STANDARD); 115 | } 116 | 117 | 118 | ntv_t * 119 | cookie_decode(cookie_engine_t *ce, const char *str) 120 | { 121 | if(ce == NULL || str == NULL) 122 | return NULL; 123 | 124 | const size_t len = strlen(str); 125 | uint8_t *bin = alloca(len); 126 | int binlen = base64_decode(bin, str, len); 127 | if(binlen == -1) 128 | return NULL; 129 | 130 | if(binlen < COOKIE_NONCE_LEN + COOKIE_TAG_LEN + 1) 131 | return NULL; 132 | 133 | pthread_mutex_lock(&ce->dec_mutex); 134 | 135 | if(EVP_DecryptInit_ex(ce->dec, NULL, NULL, NULL, bin) != 1) { 136 | pthread_mutex_unlock(&ce->dec_mutex); 137 | return NULL; 138 | } 139 | 140 | if(EVP_CIPHER_CTX_ctrl(ce->dec, EVP_CTRL_CCM_SET_TAG, COOKIE_TAG_LEN, 141 | bin + COOKIE_NONCE_LEN) != 1) { 142 | pthread_mutex_unlock(&ce->dec_mutex); 143 | return NULL; 144 | } 145 | 146 | uint8_t *plaintext = alloca(len); 147 | int outlen = 0; 148 | if(EVP_DecryptUpdate(ce->dec, plaintext, &outlen, 149 | bin + COOKIE_NONCE_LEN + COOKIE_TAG_LEN, 150 | binlen - COOKIE_NONCE_LEN - COOKIE_TAG_LEN) != 1) { 151 | pthread_mutex_unlock(&ce->dec_mutex); 152 | return NULL; 153 | } 154 | 155 | int tmplen; 156 | const int ret = EVP_DecryptFinal_ex(ce->dec, plaintext + outlen, &tmplen); 157 | pthread_mutex_unlock(&ce->dec_mutex); 158 | if(ret <= 0) 159 | return NULL; 160 | 161 | return ntv_binary_deserialize(plaintext, outlen); 162 | } 163 | -------------------------------------------------------------------------------- /cookie.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ntv.h" 4 | 5 | typedef struct cookie_engine cookie_engine_t; 6 | 7 | cookie_engine_t *cookie_engine_create(const char *secret, const char *salt, 8 | int pbkdf2_rounds); 9 | 10 | void cookie_engine_destroy(cookie_engine_t *ce); 11 | 12 | char *cookie_encode(cookie_engine_t *ce, const ntv_t *msg); 13 | 14 | ntv_t *cookie_decode(cookie_engine_t *ce, const char *str); 15 | 16 | -------------------------------------------------------------------------------- /ctrlsock.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #define _GNU_SOURCE 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "htsbuf.h" 38 | #include "trace.h" 39 | #include "ctrlsock.h" 40 | #include "cmd.h" 41 | #include "talloc.h" 42 | #include "sock.h" 43 | static int ctrlsock_fd; 44 | 45 | 46 | /** 47 | * 48 | */ 49 | static void 50 | output_callback(void *aux, const char *fmt, ...) 51 | { 52 | char buf[2048]; 53 | va_list ap; 54 | va_start(ap, fmt); 55 | vsnprintf(buf, sizeof(buf), fmt, ap); 56 | va_end(ap); 57 | 58 | int fd = *(int *)aux; 59 | 60 | struct iovec iov[3]; 61 | 62 | iov[0].iov_base = (void *)":"; 63 | iov[0].iov_len = 1; 64 | 65 | iov[1].iov_base = (void *)buf; 66 | iov[1].iov_len = strlen(buf); 67 | 68 | iov[2].iov_base = (void *)"\n"; 69 | iov[2].iov_len = 1; 70 | 71 | if(writev(fd, iov, 3) != iov[1].iov_len + 2) { 72 | trace(LOG_ERR, "Write failed on ctrl sock"); 73 | } 74 | } 75 | 76 | 77 | 78 | /** 79 | * 80 | */ 81 | static int 82 | parse_line(int fd, const char *str, const char *user) 83 | { 84 | int rval; 85 | char tmp[32]; 86 | switch(*str) { 87 | 88 | case 'X': // Execute 89 | rval = cmd_exec(str + 1, user, &output_callback, &fd); 90 | break; 91 | 92 | case 'C': // Complete 93 | rval = cmd_complete(str + 1, user, &output_callback, &fd); 94 | break; 95 | 96 | case 'c': // Advanced completion 97 | rval = cmd_complete2(str + 1, user, &output_callback, &fd); 98 | break; 99 | 100 | default: 101 | return 1; 102 | } 103 | int l = snprintf(tmp, sizeof(tmp), "%d\n", rval); 104 | return write(fd, tmp, l) != l; 105 | } 106 | 107 | 108 | /** 109 | * 110 | */ 111 | static int 112 | parse_input(int fd, htsbuf_queue_t *q, const char *user) 113 | { 114 | while(1) { 115 | int ll = htsbuf_find(q, 0x0a); 116 | if(ll == -1) 117 | return 0; 118 | 119 | char *line = alloca(ll + 1); 120 | 121 | htsbuf_read(q, line, ll); 122 | htsbuf_drop(q, 1); // Drop \n 123 | 124 | line[ll] = 0; 125 | if(parse_line(fd, line, user)) 126 | return 1; 127 | } 128 | } 129 | 130 | 131 | 132 | /** 133 | * 134 | */ 135 | static void * 136 | conn_thread(void *aux) 137 | { 138 | int fd = (intptr_t)aux; 139 | struct ucred cr; 140 | socklen_t len = sizeof(cr); 141 | char buf[1024]; 142 | 143 | if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len)) { 144 | trace(LOG_ERR, "Unable to get peer credentials -- %s", 145 | strerror(errno)); 146 | close(fd); 147 | return NULL; 148 | } 149 | 150 | struct passwd pwd; 151 | struct passwd *result; 152 | 153 | int s = getpwuid_r(cr.uid, &pwd, buf, sizeof(buf), &result); 154 | if(result == NULL) { 155 | if(s == 0) { 156 | trace(LOG_ERR, "UID %d does not exist, closing", cr.uid); 157 | } else { 158 | trace(LOG_ERR, "Failed to lookup UID %d -- %s", cr.uid, 159 | strerror(s)); 160 | } 161 | close(fd); 162 | return NULL; 163 | } 164 | 165 | trace(LOG_INFO, 166 | "Control connection from user '%s' PID:%d UID:%d GID:%d", 167 | pwd.pw_name, cr.pid, cr.uid, cr.gid); 168 | 169 | htsbuf_queue_t recvq; 170 | htsbuf_queue_init(&recvq, 0); 171 | 172 | while(1) { 173 | uint8_t buf[256]; 174 | int r = read(fd, buf, sizeof(buf)); 175 | if(r <= 0) { 176 | trace(LOG_INFO, "Connection lost from PID %d", cr.pid); 177 | break; 178 | } 179 | htsbuf_append(&recvq, buf, r); 180 | if(parse_input(fd, &recvq, pwd.pw_name)) 181 | break; 182 | talloc_cleanup(); 183 | } 184 | 185 | htsbuf_queue_flush(&recvq); 186 | 187 | close(fd); 188 | return NULL; 189 | } 190 | 191 | /** 192 | * 193 | */ 194 | static void * 195 | ctrlsock_thread(void *aux) 196 | { 197 | socklen_t siz; 198 | struct sockaddr_un sun; 199 | pthread_t tid; 200 | 201 | while(1) { 202 | siz = sizeof(struct sockaddr_un); 203 | int fd = accept(ctrlsock_fd, (struct sockaddr *)&sun, &siz); 204 | if(fd == -1) { 205 | trace(LOG_ERR, "Unable to accept ctrl socket -- %s", 206 | strerror(errno)); 207 | sleep(1); 208 | continue; 209 | } 210 | 211 | pthread_attr_t attr; 212 | pthread_attr_init(&attr); 213 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 214 | pthread_create(&tid, &attr, conn_thread, (void *)(intptr_t)fd); 215 | pthread_attr_destroy(&attr); 216 | } 217 | return NULL; 218 | } 219 | 220 | 221 | /** 222 | * 223 | */ 224 | void 225 | ctrlsock_init(const char *ctrlsockpath) 226 | { 227 | if(!strcmp(ctrlsockpath, "/dev/null")) 228 | return; 229 | 230 | int fd = libsvc_socket(AF_UNIX, SOCK_STREAM, 0); 231 | if(fd == -1) { 232 | trace(LOG_ERR, "Unable to create ctrl socket -- %s", 233 | strerror(errno)); 234 | exit(1); 235 | } 236 | 237 | struct sockaddr_un sun; 238 | 239 | memset(&sun, 0, sizeof(sun)); 240 | 241 | unlink(ctrlsockpath); 242 | 243 | sun.sun_family = AF_UNIX; 244 | strcpy(sun.sun_path, ctrlsockpath); 245 | if(bind(fd, (struct sockaddr *)&sun, sizeof(sun))) { 246 | trace(LOG_ERR, "Unable to bind ctrl socket %s -- %s", 247 | ctrlsockpath, strerror(errno)); 248 | exit(1); 249 | } 250 | 251 | if(listen(fd, 10)) { 252 | trace(LOG_ERR, "Unable to listen on ctrl socket %s -- %s", 253 | ctrlsockpath, strerror(errno)); 254 | exit(1); 255 | } 256 | 257 | chmod(ctrlsockpath, 0770); 258 | 259 | ctrlsock_fd = fd; 260 | 261 | pthread_t tid; 262 | pthread_create(&tid, NULL, ctrlsock_thread, NULL); 263 | } 264 | -------------------------------------------------------------------------------- /ctrlsock.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | void ctrlsock_init(const char *path); 27 | -------------------------------------------------------------------------------- /curlhelpers.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "curlhelpers.h" 31 | #include "redblack.h" 32 | #include "talloc.h" 33 | #include "misc.h" 34 | #include "memstream.h" 35 | #include "sock.h" 36 | #include "ntv.h" 37 | 38 | size_t 39 | libsvc_curl_waste_output(char *ptr, size_t size, size_t nmemb, void *userdata) 40 | { 41 | return size * nmemb; 42 | } 43 | 44 | 45 | /** 46 | * 47 | */ 48 | curl_socket_t 49 | libsvc_curl_sock_fn(void *clientp, 50 | curlsocktype purpose, 51 | struct curl_sockaddr *a) 52 | { 53 | return libsvc_socket(a->family, a->socktype, a->protocol); 54 | } 55 | 56 | /** 57 | * 58 | */ 59 | RB_HEAD(cache_entry_tree, cache_entry); 60 | 61 | typedef struct cache_entry { 62 | RB_ENTRY(cache_entry) ce_link; 63 | char *ce_url; 64 | char *ce_auth; 65 | 66 | char *ce_etag; 67 | time_t ce_expire; 68 | 69 | int ce_status; // If -1, request is pending 70 | 71 | char *ce_response; 72 | } cache_entry_t; 73 | 74 | static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; 75 | static pthread_cond_t cache_cond = PTHREAD_COND_INITIALIZER; 76 | static struct cache_entry_tree cache_entries; 77 | 78 | 79 | /** 80 | * 81 | */ 82 | static int 83 | cache_entry_cmp(const cache_entry_t *a, const cache_entry_t *b) 84 | { 85 | int x = strcmp(a->ce_url, b->ce_url); 86 | if(x) 87 | return x; 88 | return strcmp(a->ce_auth ?: "", b->ce_auth ?: ""); 89 | } 90 | 91 | 92 | /** 93 | * 94 | */ 95 | static size_t 96 | hdrfunc(void *ptr, size_t size, size_t nmemb, void *userdata) 97 | { 98 | cache_entry_t *ce = userdata; 99 | char *argv[2]; 100 | size_t len = size * nmemb; 101 | char *line = alloca(len + 1); 102 | memcpy(line, ptr, len); 103 | line[len] = 0; 104 | 105 | line[strcspn(line, "\n\r")] = 0; 106 | if(str_tokenize(line, argv, 2, -1) != 2) 107 | return len; 108 | char *c; 109 | if((c = strrchr(argv[0], ':')) == NULL) 110 | return len; 111 | *c = 0; 112 | 113 | if(!strcasecmp(argv[0], "etag")) { 114 | free(ce->ce_etag); 115 | ce->ce_etag = strdup(argv[1]); 116 | } 117 | 118 | if(!strcasecmp(argv[0], "cache-control")) { 119 | const char *ma = strstr(argv[1], "max-age="); 120 | if(ma != NULL) { 121 | int max_age = atoi(ma + strlen("max-age=")); 122 | ce->ce_expire = time(NULL) + max_age; 123 | } 124 | } 125 | return len; 126 | } 127 | 128 | 129 | /** 130 | * 131 | */ 132 | ntv_t * 133 | libsvc_http_json_get(const char *url, const char *auth, 134 | char *errbuf, size_t errlen) 135 | { 136 | cache_entry_t *ce; 137 | static cache_entry_t *skel; 138 | time_t now = time(NULL); 139 | 140 | pthread_mutex_lock(&cache_mutex); 141 | 142 | if(skel == NULL) 143 | skel = calloc(1, sizeof(cache_entry_t)); 144 | 145 | skel->ce_url = (char *)url; 146 | skel->ce_auth = (char *)auth; 147 | 148 | ce = RB_INSERT_SORTED(&cache_entries, skel, ce_link, 149 | cache_entry_cmp); 150 | 151 | if(ce == NULL) { 152 | // Nothing found -> New item 'skel' was inserted 153 | ce = skel; 154 | skel = NULL; 155 | 156 | ce->ce_url = strdup(url); 157 | ce->ce_auth = auth ? strdup(auth) : NULL; 158 | } 159 | 160 | while(ce->ce_status == -1) 161 | pthread_cond_wait(&cache_cond, &cache_mutex); 162 | 163 | if(ce->ce_expire > now) { 164 | 165 | if(ce->ce_status == 200) { 166 | ntv_t *m = ntv_json_deserialize(ce->ce_response, errbuf, errlen); 167 | pthread_mutex_unlock(&cache_mutex); 168 | return m; 169 | } 170 | 171 | if(ce->ce_status >= 400) { 172 | snprintf(errbuf, errlen, "HTTP Error %d", ce->ce_status); 173 | pthread_mutex_unlock(&cache_mutex); 174 | return NULL; 175 | } 176 | } 177 | 178 | ce->ce_status = -1; 179 | 180 | 181 | char *out; 182 | size_t outlen; 183 | FILE *f = open_buffer(&out, &outlen); 184 | 185 | struct curl_slist *slist = NULL; 186 | 187 | CURL *curl = curl_easy_init(); 188 | curl_easy_setopt(curl, CURLOPT_URL, url); 189 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 190 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, f); 191 | curl_easy_setopt(curl, CURLOPT_USERAGENT, "libsvc"); 192 | curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, hdrfunc); 193 | curl_easy_setopt(curl, CURLOPT_HEADERDATA, ce); 194 | 195 | slist = curl_slist_append(slist, "Accept: application/json"); 196 | 197 | if(auth != NULL) 198 | slist = curl_slist_append(slist, tsprintf("Authorization: %s", auth)); 199 | 200 | if(ce->ce_etag != NULL) 201 | slist = curl_slist_append(slist, tsprintf("If-None-Match: %s", 202 | ce->ce_etag)); 203 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); 204 | 205 | ce->ce_expire = 0; 206 | free(ce->ce_etag); 207 | ce->ce_etag = NULL; 208 | 209 | pthread_mutex_unlock(&cache_mutex); 210 | CURLcode result = curl_easy_perform(curl); 211 | curl_slist_free_all(slist); 212 | 213 | pthread_mutex_lock(&cache_mutex); 214 | 215 | pthread_cond_broadcast(&cache_cond); 216 | 217 | fwrite("", 1, 1, f); 218 | fclose(f); 219 | 220 | if(result) { 221 | snprintf(errbuf, errlen, "%s", curl_easy_strerror(result)); 222 | curl_easy_cleanup(curl); 223 | ce->ce_expire = 0; 224 | ce->ce_status = 0; 225 | 226 | free(ce->ce_response); 227 | ce->ce_response = NULL; 228 | 229 | pthread_mutex_unlock(&cache_mutex); 230 | free(out); 231 | return NULL; 232 | } 233 | 234 | long http_code = 0; 235 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); 236 | ce->ce_status = http_code; 237 | 238 | if(http_code == 304) { 239 | 240 | ce->ce_status = 200; 241 | 242 | } else if(http_code == 200) { 243 | 244 | free(ce->ce_response); 245 | ce->ce_response = out; 246 | out = NULL; 247 | 248 | } else { 249 | 250 | snprintf(errbuf, errlen, "HTTP Error %lu", http_code); 251 | free(ce->ce_response); 252 | ce->ce_response = NULL; 253 | 254 | } 255 | 256 | free(out); 257 | curl_easy_cleanup(curl); 258 | 259 | ntv_t *m = NULL; 260 | if(ce->ce_response != NULL) 261 | m = ntv_json_deserialize(ce->ce_response, errbuf, errlen); 262 | 263 | pthread_mutex_unlock(&cache_mutex); 264 | return m; 265 | } 266 | 267 | -------------------------------------------------------------------------------- /curlhelpers.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | struct ntv; 30 | size_t libsvc_curl_waste_output(char *ptr, size_t size, size_t nmemb, 31 | void *userdata); 32 | 33 | curl_socket_t libsvc_curl_sock_fn(void *clientp, 34 | curlsocktype purpose, 35 | struct curl_sockaddr *a); 36 | 37 | 38 | struct ntv *libsvc_http_json_get(const char *url, const char *auth, 39 | char *errbuf, size_t errlen); 40 | -------------------------------------------------------------------------------- /db.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include "queue.h" 27 | 28 | #define DB_STORE_RESULT 0x1 29 | 30 | #define DB_RESULT_TAG_STR 1 31 | #define DB_RESULT_TAG_INT 2 32 | #define DB_RESULT_TAG_TIME 3 33 | 34 | #define DB_RESULT_STRING(x) DB_RESULT_TAG_STR, x, sizeof(x) 35 | #define DB_RESULT_INT(x) DB_RESULT_TAG_INT, &x 36 | #define DB_RESULT_TIME(x) DB_RESULT_TAG_TIME, &x 37 | 38 | 39 | typedef enum { 40 | DB_ERR_NO_DATA = 1, 41 | DB_ERR_OK = 0, 42 | DB_ERR_OTHER = -1, 43 | DB_ERR_DEADLOCK = -2, 44 | } db_err_t; 45 | 46 | 47 | typedef struct db_stmt db_stmt_t; 48 | typedef struct db_conn db_conn_t; 49 | typedef struct db_args { 50 | char type; 51 | int len; 52 | union { 53 | const char *str; 54 | int i32; 55 | }; 56 | } db_args_t; 57 | 58 | 59 | db_conn_t *db_get_conn(void); 60 | 61 | void db_init(void); 62 | 63 | db_stmt_t *db_stmt_get(db_conn_t *c, const char *str); 64 | 65 | void db_stmt_reset(db_stmt_t *s); 66 | 67 | db_err_t db_stmt_exec(db_stmt_t *s, const char *fmt, ...); 68 | 69 | db_err_t db_stmt_execa(db_stmt_t *stmt, int argc, const db_args_t *argv); 70 | 71 | int db_stmt_affected_rows(db_stmt_t *s); 72 | 73 | int db_stream_row(int flags, db_stmt_t *s, ...) 74 | __attribute__ ((warn_unused_result)); 75 | 76 | db_stmt_t *db_stmt_prep(const char *sql); 77 | 78 | void db_stmt_cleanup(db_stmt_t **ptr); 79 | 80 | #define scoped_db_stmt(x, sql) \ 81 | db_stmt_t *x __attribute__((cleanup(db_stmt_cleanup)))=db_stmt_prep(sql); 82 | 83 | db_err_t db_begin(db_conn_t *c) 84 | __attribute__ ((warn_unused_result)); 85 | 86 | int db_commit(db_conn_t *c); 87 | 88 | int db_rollback(db_conn_t *c); 89 | 90 | int db_upgrade_schema(const char *schema_bundle); 91 | -------------------------------------------------------------------------------- /dbl.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | double my_str2double(const char *str, const char **endp); 27 | 28 | int my_double2str(char *buf, size_t bufsize, double realvalue, 29 | int precision, int type); 30 | 31 | #define DBL_TYPE_GENERIC 0 32 | #define DBL_TYPE_FLOAT 1 33 | #define DBL_TYPE_EXP 2 34 | -------------------------------------------------------------------------------- /dial.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "dial.h" 39 | #include "sock.h" 40 | #include "trace.h" 41 | 42 | #include 43 | #include 44 | 45 | /** 46 | * 47 | */ 48 | static int 49 | getstreamsocket(int family) 50 | { 51 | int fd; 52 | int val = 1; 53 | 54 | fd = libsvc_socket(family, SOCK_STREAM, 0); 55 | if(fd == -1) 56 | return -errno; 57 | 58 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); 59 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); 60 | return fd; 61 | } 62 | 63 | 64 | 65 | 66 | 67 | static int 68 | dial_one(const struct sockaddr *sa, socklen_t slen, int timeout, 69 | const char *hostname, char *errbuf, size_t errlen, 70 | int debug) 71 | { 72 | char addrtxt[512]; 73 | int err; 74 | socklen_t sockerrlen = sizeof(err); 75 | 76 | switch(sa->sa_family) { 77 | case AF_INET: 78 | inet_ntop(AF_INET, &((const struct sockaddr_in *)sa)->sin_addr, 79 | addrtxt, sizeof(addrtxt)); 80 | break; 81 | case AF_INET6: 82 | inet_ntop(AF_INET6, &((const struct sockaddr_in6 *)sa)->sin6_addr, 83 | addrtxt, sizeof(addrtxt)); 84 | break; 85 | default: 86 | snprintf(errbuf, errlen, "Invalid address family %d", sa->sa_family); 87 | return -1; 88 | } 89 | 90 | int fd = getstreamsocket(sa->sa_family); 91 | int r = connect(fd, sa, slen); 92 | if(r == -1) { 93 | if(debug) 94 | trace(LOG_DEBUG, "dialfd: connect() = %s", strerror(errno)); 95 | if(errno == EINPROGRESS) { 96 | struct pollfd pfd; 97 | 98 | pfd.fd = fd; 99 | pfd.events = POLLOUT; 100 | pfd.revents = 0; 101 | 102 | r = poll(&pfd, 1, timeout); 103 | if(debug) 104 | trace(LOG_DEBUG, "dialfd: poll returned %d", r); 105 | if(r == 0) { 106 | /* Timeout */ 107 | close(fd); 108 | snprintf(errbuf, errlen, "Connection to %s timed out", 109 | addrtxt); 110 | return -1; 111 | } 112 | 113 | if(r == -1) { 114 | snprintf(errbuf, errlen, "Connection to %s failed -- %s", 115 | addrtxt, strerror(errno)); 116 | close(fd); 117 | return -1; 118 | } 119 | 120 | getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &sockerrlen); 121 | } else { 122 | err = errno; 123 | } 124 | } else { 125 | err = 0; 126 | } 127 | 128 | if(err != 0) { 129 | close(fd); 130 | snprintf(errbuf, errlen, "Connection to %s failed -- %s", 131 | addrtxt, strerror(err)); 132 | return -1; 133 | } 134 | 135 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); 136 | 137 | int val = 1; 138 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); 139 | 140 | val = 1; 141 | setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); 142 | 143 | #ifdef TCP_KEEPIDLE 144 | val = 30; 145 | setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)); 146 | #endif 147 | 148 | #ifdef TCP_KEEPINTVL 149 | val = 15; 150 | setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)); 151 | #endif 152 | 153 | #ifdef TCP_KEEPCNT 154 | val = 5; 155 | setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); 156 | #endif 157 | 158 | return fd; 159 | } 160 | 161 | 162 | /** 163 | * 164 | */ 165 | int 166 | dialfd(const char *hostname, int port, int timeout, 167 | char *errbuf, size_t errlen, int debug) 168 | { 169 | char service[10]; 170 | snprintf(service, sizeof(service), "%u", port); 171 | struct addrinfo *res = NULL; 172 | if(debug) 173 | trace(LOG_DEBUG, "dialfd: Resolveing %s:%s", hostname, service); 174 | const int gai_err = getaddrinfo(hostname, service, NULL, &res); 175 | if(gai_err) { 176 | snprintf(errbuf, errlen, "Unable to resolve %s -- %s", hostname, 177 | gai_strerror(gai_err)); 178 | return -1; 179 | } 180 | if(debug) 181 | trace(LOG_DEBUG, "dialfd: Resolved %s:%s", hostname, service); 182 | 183 | const struct addrinfo *ai = res; 184 | int fd = -1; 185 | while(ai) { 186 | if(debug) 187 | trace(LOG_DEBUG, "dialfd: %s:%s Attempting to connect", 188 | hostname, service); 189 | fd = dial_one(ai->ai_addr, ai->ai_addrlen, timeout, hostname, 190 | errbuf, errlen, debug); 191 | if(debug) 192 | trace(LOG_DEBUG, "dialfd: %s:%s Attempting to connect, fd=%d", 193 | hostname, service, fd); 194 | if(fd >= 0) 195 | break; 196 | ai = ai->ai_next; 197 | } 198 | freeaddrinfo(res); 199 | return fd; 200 | } 201 | 202 | 203 | /** 204 | * 205 | */ 206 | tcp_stream_t * 207 | dial(const char *hostname, int port, int timeout, const tcp_ssl_info_t *tsi, 208 | char *errbuf, size_t errlen) 209 | { 210 | int fd = dialfd(hostname, port, timeout, errbuf, errlen, 211 | tsi != NULL ? tsi->debug : 0); 212 | if(fd == -1) 213 | return NULL; 214 | 215 | if(tsi != NULL) { 216 | #if defined(WITH_OPENSSL) 217 | return tcp_stream_create_ssl_from_fd(fd, hostname, tsi, 218 | errbuf, errlen); 219 | #endif 220 | snprintf(errbuf, errlen, "Not build with SSL"); 221 | close(fd); 222 | return NULL; 223 | } 224 | return tcp_stream_create_from_fd(fd); 225 | } 226 | -------------------------------------------------------------------------------- /dial.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include "tcp.h" 27 | 28 | tcp_stream_t *dial(const char *hostname, int port, int timeout, 29 | const tcp_ssl_info_t *tsi, char *errbuf, size_t errlen); 30 | 31 | int dialfd(const char *hostname, int port, int timeout, 32 | char *errbuf, size_t errlen, int debug); 33 | -------------------------------------------------------------------------------- /dns.c: -------------------------------------------------------------------------------- 1 | #include "dns.h" 2 | 3 | #include "strvec.h" 4 | #include "bytestream.h" 5 | 6 | #include 7 | 8 | int 9 | dns_parse_name(strvec_t *out, size_t offset, 10 | const uint8_t *pkt, size_t pktlen) 11 | { 12 | int used = 0; 13 | size_t prev_offset = offset; 14 | 15 | while(1) { 16 | if(offset + used + 1 > pktlen) { 17 | return 0; 18 | } 19 | 20 | int labellen = pkt[offset + used]; 21 | if(labellen >= 0xc0) 22 | break; 23 | if(labellen >= 0x40) 24 | return 0; 25 | used++; 26 | if(labellen == 0) 27 | return used; 28 | 29 | if(offset + used >= pktlen) { 30 | return 0; 31 | } 32 | strvec_pushl(out, (const char *)pkt + offset + used, labellen); 33 | used += labellen; 34 | } 35 | 36 | offset += used; 37 | used += 2; 38 | 39 | while(1) { 40 | if(offset > pktlen) 41 | return 0; 42 | size_t off = rd16_be(pkt + offset) & 0x3fff; 43 | if(off >= prev_offset) 44 | return 0; 45 | offset = off; 46 | prev_offset = off; 47 | 48 | while(1) { 49 | if(offset + 1 > pktlen) { 50 | return 0; 51 | } 52 | 53 | int labellen = pkt[offset]; 54 | if(labellen >= 0xc0) 55 | break; 56 | if(labellen >= 0x40) 57 | return 0; 58 | offset++; 59 | if(labellen == 0) 60 | return used; 61 | 62 | if(offset >= pktlen) { 63 | return 0; 64 | } 65 | strvec_pushl(out, (const char *)pkt + offset, labellen); 66 | offset += labellen; 67 | } 68 | } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /dns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct strvec; 7 | 8 | int dns_parse_name(struct strvec *out, size_t offset, 9 | const uint8_t *pkt, size_t pktlen); 10 | -------------------------------------------------------------------------------- /err.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "err.h" 7 | #include "misc.h" 8 | #include "strvec.h" 9 | 10 | void 11 | err_push(err_t **p, const char *fmt, ...) 12 | { 13 | if(p == NULL) 14 | return; 15 | va_list ap; 16 | va_start(ap, fmt); 17 | err_t *e = calloc(1, sizeof(err_t)); 18 | e->err_msg = fmtv(fmt, ap); 19 | va_end(ap); 20 | e->err_prev = *p; 21 | *p = e; 22 | } 23 | 24 | void 25 | err_pushsys(err_t **p, const char *fmtstr, ...) 26 | { 27 | if(p == NULL) 28 | return; 29 | 30 | const int syserr = errno; 31 | 32 | va_list ap; 33 | va_start(ap, fmtstr); 34 | err_t *e = calloc(1, sizeof(err_t)); 35 | scoped_char *x = fmtv(fmtstr, ap); 36 | va_end(ap); 37 | 38 | e->err_syserr = syserr; 39 | e->err_msg = fmt("%s -- %s", x, strerror(syserr)); 40 | e->err_prev = *p; 41 | *p = e; 42 | } 43 | 44 | 45 | void 46 | err_release(err_t **p) 47 | { 48 | err_t *e, *n; 49 | for(e = *p; e != NULL; e = n) { 50 | n = e->err_prev; 51 | free(e->err_msg); 52 | free(e); 53 | } 54 | *p = NULL; 55 | } 56 | 57 | char * 58 | err_str(const err_t *e) 59 | { 60 | if(e == NULL) 61 | return NULL; 62 | 63 | scoped_strvec(v); 64 | for(; e != NULL; e = e->err_prev) { 65 | strvec_push(&v, e->err_msg); 66 | } 67 | 68 | return strvec_join(&v, " because "); 69 | } 70 | -------------------------------------------------------------------------------- /err.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct err { 4 | struct err *err_prev; 5 | char *err_msg; 6 | int err_syserr; 7 | } err_t; 8 | 9 | void err_push(err_t **p, const char *fmt, ...); 10 | 11 | void err_pushsys(err_t **p, const char *fmt, ...); 12 | 13 | void err_release(err_t **p); 14 | 15 | char *err_str(const err_t *p); 16 | 17 | #define scoped_err_t err_t __attribute__((cleanup(err_release))) 18 | -------------------------------------------------------------------------------- /filebundle.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | struct filebundle_entry { 25 | const char *filename; 26 | const unsigned char *data; 27 | int size; 28 | int original_size; 29 | }; 30 | 31 | struct filebundle { 32 | struct filebundle *next; 33 | const struct filebundle_entry *entries; 34 | const char *prefix; 35 | }; 36 | 37 | int filebundle_load(const char *p, void **ptr, int *len, int *osize); 38 | 39 | void filebundle_free(void *ptr); 40 | -------------------------------------------------------------------------------- /filebundle_disk.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include "filebundle.h" 33 | #include "misc.h" 34 | 35 | 36 | 37 | int 38 | filebundle_load(const char *p, void **ptr, int *len, int *osize) 39 | { 40 | if(osize != NULL) 41 | *osize = -1; 42 | 43 | int fd = open(p, O_RDONLY); 44 | if(fd == -1) 45 | return errno; 46 | 47 | struct stat st; 48 | if(fstat(fd, &st)) { 49 | int err = errno; 50 | close(fd); 51 | return err; 52 | } 53 | 54 | if(ptr == NULL) 55 | return 0; 56 | 57 | void *mem = malloc(st.st_size); 58 | if(read(fd, mem, st.st_size) != st.st_size) { 59 | int err = errno; 60 | close(fd); 61 | free(mem); 62 | return err; 63 | } 64 | close(fd); 65 | 66 | *ptr = mem; 67 | if(len) 68 | *len = st.st_size; 69 | return 0; 70 | } 71 | 72 | 73 | void 74 | filebundle_free(void *ptr) 75 | { 76 | free(ptr); 77 | } 78 | -------------------------------------------------------------------------------- /filebundle_embedded.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2013 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include "filebundle.h" 30 | #include "misc.h" 31 | 32 | struct filebundle *filebundles; 33 | 34 | 35 | int 36 | filebundle_load(const char *p, void **ptr, int *len, int *osize) 37 | { 38 | const struct filebundle_entry *fe; 39 | const struct filebundle *fb; 40 | char *path = mystrdupa(p); 41 | 42 | char *x = strchr(path, '/'); 43 | if(x == NULL) 44 | return ENOTDIR; 45 | 46 | *x++ = 0; 47 | for(fb = filebundles; fb != NULL; fb = fb->next) { 48 | if(!strcmp(path, fb->prefix)) 49 | break; 50 | } 51 | if(fb == NULL) 52 | return ENODEV; 53 | 54 | for(fe = fb->entries; fe->filename != NULL; fe++) { 55 | if(!strcmp(fe->filename, x)) 56 | break; 57 | } 58 | 59 | if(fe->filename == NULL) 60 | return ENOENT; 61 | 62 | if(ptr) 63 | *ptr = (void *)fe->data; 64 | if(len) 65 | *len = fe->size; 66 | if(osize) 67 | *osize = fe->original_size; 68 | 69 | return 0; 70 | } 71 | 72 | 73 | void 74 | filebundle_free(void *ptr) 75 | { 76 | // NOP for embedded stuff 77 | } 78 | -------------------------------------------------------------------------------- /fpipe.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "mbuf.h" 11 | #include "fpipe.h" 12 | #include "atomic.h" 13 | #include "misc.h" 14 | 15 | struct fpipe { 16 | 17 | pthread_mutex_t p_mutex; 18 | pthread_cond_t p_cond; 19 | 20 | mbuf_t p_buffer; 21 | 22 | int p_need; 23 | 24 | uint8_t p_open; 25 | uint8_t p_eof; 26 | uint8_t p_err; 27 | }; 28 | 29 | 30 | static void 31 | pipe_destroy(fpipe_t *p) 32 | { 33 | assert(p->p_eof == 1 && p->p_open == 0); 34 | mbuf_clear(&p->p_buffer); 35 | pthread_mutex_unlock(&p->p_mutex); 36 | free(p); 37 | } 38 | 39 | 40 | static int 41 | pipe_write(void *aux, const char *data, int size) 42 | { 43 | fpipe_t *p = aux; 44 | 45 | pthread_mutex_lock(&p->p_mutex); 46 | while(p->p_buffer.mq_size > p->p_need && p->p_open) 47 | pthread_cond_wait(&p->p_cond, &p->p_mutex); 48 | mbuf_append(&p->p_buffer, data, size); 49 | pthread_cond_signal(&p->p_cond); 50 | pthread_mutex_unlock(&p->p_mutex); 51 | 52 | if(!p->p_open) 53 | return 0; 54 | return size; 55 | } 56 | 57 | static int 58 | pipe_write_close(void *aux) 59 | { 60 | fpipe_t *p = aux; 61 | pthread_mutex_lock(&p->p_mutex); 62 | p->p_eof = 1; 63 | if(p->p_open) { 64 | pthread_cond_signal(&p->p_cond); 65 | pthread_mutex_unlock(&p->p_mutex); 66 | return 0; 67 | } 68 | pipe_destroy(p); 69 | return 0; 70 | } 71 | 72 | 73 | #ifndef __APPLE__ 74 | static ssize_t 75 | pipe_write2(void *cookie, const char *buf, size_t size) 76 | { 77 | return pipe_write(cookie, buf, size); 78 | } 79 | 80 | 81 | static cookie_io_functions_t pipe_write_functions = { 82 | .write = pipe_write2, 83 | .close = pipe_write_close, 84 | }; 85 | #endif 86 | 87 | 88 | 89 | 90 | static int 91 | pipe_read(void *aux, char *data, int size) 92 | { 93 | fpipe_t *p = aux; 94 | 95 | pthread_mutex_lock(&p->p_mutex); 96 | p->p_need = MIN(size, 65536); 97 | pthread_cond_signal(&p->p_cond); 98 | while(!p->p_eof && p->p_buffer.mq_size < p->p_need && !p->p_err) { 99 | pthread_cond_wait(&p->p_cond, &p->p_mutex); 100 | } 101 | 102 | if(p->p_err) { 103 | p->p_need = 0; 104 | pthread_mutex_unlock(&p->p_mutex); 105 | return -1; 106 | } 107 | 108 | int r = mbuf_read(&p->p_buffer, data, size); 109 | pthread_cond_signal(&p->p_cond); 110 | pthread_mutex_unlock(&p->p_mutex); 111 | return r; 112 | } 113 | 114 | 115 | static int 116 | pipe_read_close(void *aux) 117 | { 118 | fpipe_t *p = aux; 119 | 120 | pthread_mutex_lock(&p->p_mutex); 121 | p->p_open = 0; 122 | if(!p->p_eof) { 123 | pthread_cond_signal(&p->p_cond); 124 | pthread_mutex_unlock(&p->p_mutex); 125 | return 0; 126 | } 127 | pipe_destroy(p); 128 | return 0; 129 | } 130 | 131 | 132 | 133 | 134 | 135 | #ifndef __APPLE__ 136 | 137 | static ssize_t 138 | pipe_read2(void *fh, char *buf, size_t size) 139 | { 140 | return pipe_read(fh, buf, size); 141 | } 142 | 143 | 144 | static cookie_io_functions_t pipe_read_functions = { 145 | .read = pipe_read2, 146 | .close = pipe_read_close, 147 | }; 148 | #endif 149 | 150 | 151 | fpipe_t * 152 | fpipe(FILE **reader, FILE **writer) 153 | { 154 | fpipe_t *p = calloc(1, sizeof(fpipe_t)); 155 | pthread_mutex_init(&p->p_mutex, NULL); 156 | pthread_cond_init(&p->p_cond, NULL); 157 | p->p_open = 1; 158 | mbuf_init(&p->p_buffer); 159 | 160 | #ifdef __APPLE__ 161 | *reader = funopen(p, pipe_read, NULL, NULL, pipe_read_close); 162 | #else 163 | *reader = fopencookie(p, "rb", pipe_read_functions); 164 | #endif 165 | if(*reader != NULL) { 166 | setvbuf(*reader, NULL, _IOFBF, 65536); 167 | } 168 | 169 | #ifdef __APPLE__ 170 | *writer = funopen(p, NULL, pipe_write, NULL, pipe_write_close); 171 | #else 172 | *writer = fopencookie(p, "wb", pipe_write_functions); 173 | #endif 174 | 175 | return p; 176 | } 177 | 178 | void 179 | fpipe_set_error(fpipe_t *p) 180 | { 181 | pthread_mutex_lock(&p->p_mutex); 182 | p->p_err = 1; 183 | pthread_cond_signal(&p->p_cond); 184 | pthread_mutex_unlock(&p->p_mutex); 185 | } 186 | -------------------------------------------------------------------------------- /fpipe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct fpipe fpipe_t; 6 | 7 | fpipe_t *fpipe(FILE **reader, FILE **writer); 8 | 9 | void fpipe_set_error(fpipe_t *fp); 10 | -------------------------------------------------------------------------------- /gcp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cfg.h" 6 | #include "ntv.h" 7 | #include "murmur3.h" 8 | #include "misc.h" 9 | #include "trace.h" 10 | #include "http_client.h" 11 | #include "gcp.h" 12 | 13 | typedef struct token_pair { 14 | pthread_mutex_t mutex; 15 | char *refresh_token; 16 | char *access_token; 17 | time_t expire; 18 | } token_pair_t; 19 | 20 | #define TOKEN_CACHE_SIZE 512 21 | 22 | static token_pair_t tokenpairs[TOKEN_CACHE_SIZE]; 23 | 24 | char * 25 | gcp_get_access_token_from_refresh(const char *refresh_token, int force) 26 | { 27 | cfg_root(pc); 28 | 29 | const char *clientid = cfg_get_str(pc, CFG("google", "clientid"), NULL); 30 | const char *secret = cfg_get_str(pc, CFG("google", "clientsecret"), NULL); 31 | 32 | if(clientid == NULL || secret == NULL) { 33 | return NULL; 34 | } 35 | 36 | const unsigned int hash = 37 | MurHash3_32(refresh_token, strlen(refresh_token), 0) & 38 | (TOKEN_CACHE_SIZE - 1); 39 | 40 | token_pair_t *tp = &tokenpairs[hash]; 41 | time_t now = time(NULL); 42 | 43 | pthread_mutex_lock(&tp->mutex); 44 | 45 | if(!force && tp->refresh_token != NULL && tp->expire > now && 46 | !strcmp(tp->refresh_token, refresh_token)) { 47 | char *r = strdup(tp->access_token); 48 | pthread_mutex_unlock(&tp->mutex); 49 | return r; 50 | } 51 | 52 | scoped_char *params = fmt("client_id=%s" 53 | "&client_secret=%s" 54 | "&refresh_token=%s" 55 | "&grant_type=refresh_token", 56 | clientid, 57 | secret, 58 | refresh_token); 59 | 60 | scoped_http_result(result); 61 | if(http_client_request(&result, 62 | "https://www.googleapis.com/oauth2/v4/token", 63 | HCR_FLAGS(HCR_DECODE_BODY_AS_JSON), 64 | HCR_POSTFIELDS(params, strlen(params)), 65 | HCR_TIMEOUT(10), 66 | NULL)) { 67 | trace(LOG_ERR, "gcp: unable to refresh access token -- %s", 68 | result.hcr_transport_status); 69 | pthread_mutex_unlock(&tp->mutex); 70 | return NULL; 71 | } 72 | 73 | const char *at = ntv_get_str(result.hcr_json_result, "access_token"); 74 | if(at != NULL) { 75 | int expire = ntv_get_int(result.hcr_json_result, "expires_in", 3600); 76 | tp->expire = now + expire - 1; 77 | free(tp->access_token); 78 | tp->access_token = strdup(at); 79 | } 80 | 81 | pthread_mutex_unlock(&tp->mutex); 82 | 83 | return strdup(at); 84 | } 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /gcp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | char *gcp_get_access_token_from_refresh(const char *refresh_token, int force); 4 | -------------------------------------------------------------------------------- /gitver.mk: -------------------------------------------------------------------------------- 1 | 2 | ifeq ($(shell uname),Darwin) 3 | GITVER_MD5 := md5 4 | else 5 | GITVER_MD5 := md5sum 6 | endif 7 | 8 | GITVER_VARGUARD = $(1)_GUARD_$(shell echo $($(1)) | ${GITVER_MD5} | cut -d ' ' -f 1) 9 | 10 | GIT_DESCRIBE_OUTPUT ?= $(shell git describe --always --dirty) 11 | 12 | ${BUILDDIR}/version_git.h: ${BUILDDIR}/gitver/$(call GITVER_VARGUARD,GIT_DESCRIBE_OUTPUT) 13 | @echo >$@ "#ifndef VERSION_GIT" 14 | @echo >>$@ "#define VERSION_GIT \"${GIT_DESCRIBE_OUTPUT}\"" 15 | @echo >>$@ "#endif" 16 | 17 | ${BUILDDIR}/gitver/$(call GITVER_VARGUARD,GIT_DESCRIBE_OUTPUT): 18 | @rm -rf "${BUILDDIR}/gitver" 19 | @mkdir -p "${BUILDDIR}/gitver" 20 | @touch $@ 21 | -------------------------------------------------------------------------------- /htsbuf.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifndef HTSBUF_H__ 25 | #define HTSBUF_H__ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | TAILQ_HEAD(htsbuf_data_queue, htsbuf_data); 33 | 34 | typedef struct htsbuf_data { 35 | TAILQ_ENTRY(htsbuf_data) hd_link; 36 | uint8_t *hd_data; 37 | unsigned int hd_data_size; /* Size of allocation hb_data */ 38 | unsigned int hd_data_len; /* Number of valid bytes from hd_data */ 39 | unsigned int hd_data_off; /* Offset in data, used for partial writes */ 40 | } htsbuf_data_t; 41 | 42 | typedef struct htsbuf_queue { 43 | struct htsbuf_data_queue hq_q; 44 | unsigned int hq_size; 45 | unsigned int hq_alloc_size; 46 | } htsbuf_queue_t; 47 | 48 | void htsbuf_queue_init(htsbuf_queue_t *hq, unsigned int maxsize); 49 | 50 | void htsbuf_queue_init2(htsbuf_queue_t *hq, unsigned int alloc_size); 51 | 52 | void htsbuf_queue_flush(htsbuf_queue_t *hq); 53 | 54 | void htsbuf_vqprintf(htsbuf_queue_t *hq, const char *fmt, va_list ap); 55 | 56 | void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...); 57 | 58 | void htsbuf_append(htsbuf_queue_t *hq, const void *buf, size_t len); 59 | 60 | void htsbuf_append_prealloc(htsbuf_queue_t *hq, const void *buf, size_t len); 61 | 62 | void htsbuf_data_free(htsbuf_queue_t *hq, htsbuf_data_t *hd); 63 | 64 | size_t htsbuf_read(htsbuf_queue_t *hq, void *buf, size_t len); 65 | 66 | size_t htsbuf_peek(htsbuf_queue_t *hq, void *buf, size_t len); 67 | 68 | size_t htsbuf_drop(htsbuf_queue_t *hq, size_t len); 69 | 70 | int htsbuf_find(htsbuf_queue_t *hq, uint8_t v); 71 | 72 | void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src); 73 | 74 | void htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *str); 75 | 76 | void htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s); 77 | 78 | void htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *s); 79 | 80 | void htsbuf_dump_raw_stderr(htsbuf_queue_t *hq); 81 | 82 | char *htsbuf_to_string(htsbuf_queue_t *hq); 83 | 84 | struct rstr; 85 | struct rstr *htsbuf_to_rstr(htsbuf_queue_t *hq, const char *prefix); 86 | 87 | void htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix); 88 | 89 | #endif /* HTSBUF_H__ */ 90 | -------------------------------------------------------------------------------- /http.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andoma/libsvc/30e9b790016bf64c753f53a151e48cc2853a8395/http.c -------------------------------------------------------------------------------- /http.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andoma/libsvc/30e9b790016bf64c753f53a151e48cc2853a8395/http.h -------------------------------------------------------------------------------- /http_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct ntv; 6 | 7 | typedef struct http_client_response { 8 | 9 | char *hcr_body; 10 | size_t hcr_bodysize; 11 | 12 | struct ntv *hcr_json_result; 13 | 14 | struct ntv *hcr_headers; 15 | 16 | struct ntv *hcr_headers_listified; 17 | 18 | int hcr_http_status; 19 | 20 | int hcr_malformed_json; 21 | 22 | int hcr_local_error; 23 | 24 | const char *hcr_transport_status; 25 | 26 | char hcr_errbuf[32]; 27 | 28 | char *hcr_primary_ip; 29 | 30 | } http_client_response_t; 31 | 32 | typedef const char *(http_client_auth_cb_t)(void *opaque, int http_status, 33 | const char *authenticate_header); 34 | 35 | 36 | enum { 37 | HCR_TAG_END, 38 | HCR_TAG_ERRBUF, 39 | HCR_TAG_ERR, 40 | HCR_TAG_FLAGS, 41 | HCR_TAG_HEADER, 42 | HCR_TAG_TIMEOUT, 43 | HCR_TAG_PUTDATA, 44 | HCR_TAG_POSTDATA, 45 | HCR_TAG_POSTFIELDS, 46 | HCR_TAG_POSTJSON, 47 | HCR_TAG_POSTARGS, 48 | HCR_TAG_AUTHCB, 49 | HCR_TAG_VERB, 50 | HCR_TAG_USERNPASS, 51 | HCR_TAG_OUTPUTFILE, 52 | HCR_TAG_POSTFILE, 53 | HCR_TAG_MULTIPARTFILE, 54 | HCR_TAG_MIN_SPEED, 55 | HCR_TAG_HTTP_PROXY, 56 | }; 57 | 58 | 59 | #define HCR_DECODE_BODY_AS_JSON 0x1 60 | #define HCR_NO_FAIL_ON_ERROR 0x2 61 | #define HCR_NO_FOLLOW_REDIRECT 0x4 62 | #define HCR_VERBOSE 0x8 63 | #define HCR_ACCEPT_GZIP 0x10 64 | #define HCR_READ_FILE_LOG_ERRORS 0x20 65 | 66 | #define HCR_ERRBUF(a, b) HCR_TAG_ERRBUF, a, (size_t)(b) 67 | #define HCR_ERR(a) HCR_TAG_ERR, a 68 | #define HCR_FLAGS(a) HCR_TAG_FLAGS, a 69 | #define HCR_HEADER(a, b) HCR_TAG_HEADER, a, b 70 | #define HCR_TIMEOUT(a) HCR_TAG_TIMEOUT, a 71 | #define HCR_PUTDATA(data, len, ct) HCR_TAG_PUTDATA, data, (size_t)(len), ct 72 | #define HCR_POSTDATA(data, len, ct) HCR_TAG_POSTDATA, data, (size_t)(len), ct 73 | #define HCR_POSTFIELDS(data, len) HCR_TAG_POSTFIELDS, data, (size_t)(len) 74 | #define HCR_POSTJSON(ntv) HCR_TAG_POSTJSON, ntv 75 | #define HCR_POSTARGS(ntv) HCR_TAG_POSTARGS, ntv 76 | #define HCR_POSTFILE(file, ct) HCR_TAG_POSTFILE, file, ct 77 | #define HCR_AUTHCB(cb, opaque) HCR_TAG_AUTHCB, cb, opaque 78 | #define HCR_VERB(v) HCR_TAG_VERB, v 79 | #define HCR_USERNPASS(a, b) HCR_TAG_USERNPASS, a, b 80 | #define HCR_OUTPUTFILE(a) HCR_TAG_OUTPUTFILE, a 81 | #define HCR_MULTIPARTFILE(a,b,c) HCR_TAG_MULTIPARTFILE, a, b, c 82 | #define HCR_MIN_SPEED(a) HCR_TAG_MIN_SPEED, a 83 | #define HCR_HTTP_PROXY(a) HCR_TAG_HTTP_PROXY, a 84 | 85 | int http_client_request(http_client_response_t *hcr, const char *url, ...) 86 | __attribute__((__sentinel__(0))); 87 | 88 | void http_client_response_free(http_client_response_t *hcr); 89 | 90 | #define scoped_http_result(x) \ 91 | http_client_response_t x \ 92 | __attribute__((cleanup(http_client_response_free))) = {} 93 | 94 | FILE *http_open_file(const char *url); 95 | 96 | FILE *http_read_file(const char *url, void *opaque, 97 | http_client_auth_cb_t *auth_cb, int flags); 98 | 99 | FILE *http_read_file_va(const char *url, ...) 100 | __attribute__((__sentinel__(0))); 101 | 102 | 103 | int http_client_get_http_code(void *handle); 104 | 105 | char *http_client_ntv_to_args(const struct ntv *ntv); 106 | -------------------------------------------------------------------------------- /init.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2016 Andreas Smas 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #define LIBSVC_GLUE(a, b) a ## b 25 | #define LIBSVC_JOIN(a, b) LIBSVC_GLUE(a, b) 26 | 27 | 28 | void inithelper_register(void (*init)(void), 29 | void (*term)(void), 30 | void (*fini)(void), 31 | int prio); 32 | 33 | 34 | 35 | // Lower prio == init earlier 36 | 37 | #define INITME(init_, fini_, prio_) \ 38 | static void LIBSVC_JOIN(init, __LINE__)(void) __attribute__((constructor)); \ 39 | static void LIBSVC_JOIN(init, __LINE__)(void) { \ 40 | inithelper_register(init_, NULL, fini_, prio_); \ 41 | } 42 | 43 | #define INITME4(init_, term_, fini_, prio_) \ 44 | static void LIBSVC_JOIN(init, __LINE__)(void) __attribute__((constructor)); \ 45 | static void LIBSVC_JOIN(init, __LINE__)(void) { \ 46 | inithelper_register(init_, term_, fini_, prio_); \ 47 | } 48 | -------------------------------------------------------------------------------- /intvec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "intvec.h" 6 | 7 | 8 | void 9 | intvec_push(intvec_t *vec, int value) 10 | { 11 | if(vec->count + 1 >= vec->capacity) { 12 | vec->capacity = vec->capacity * 2 + 16; 13 | vec->v = realloc(vec->v, sizeof(vec->v[0]) * vec->capacity); 14 | } 15 | vec->v[vec->count++] = value; 16 | } 17 | 18 | void 19 | intvec_reset(intvec_t *vec) 20 | { 21 | vec->count = 0; 22 | vec->capacity = 0; 23 | free(vec->v); 24 | vec->v = NULL; 25 | } 26 | 27 | void 28 | intvec_insert(intvec_t *vec, int position, int value) 29 | { 30 | if(position == vec->count) 31 | return intvec_push(vec, value); 32 | 33 | if(vec->count + 1 >= vec->capacity) { 34 | vec->capacity = vec->capacity * 2 + 16; 35 | vec->v = realloc(vec->v, sizeof(vec->v[0]) * vec->capacity); 36 | } 37 | 38 | memmove(vec->v + position + 1, vec->v + position, 39 | (vec->count - position) * sizeof(vec->v[0])); 40 | 41 | vec->v[position] = value; 42 | vec->count++; 43 | } 44 | 45 | 46 | void 47 | intvec_delete(intvec_t *vec, unsigned int position) 48 | { 49 | assert(position < vec->count); 50 | memmove(vec->v + position, vec->v + position + 1, 51 | (vec->count - position - 1) * sizeof(vec->v[0])); 52 | vec->count--; 53 | } 54 | 55 | 56 | static int 57 | intvec_search(const intvec_t *vec, int value) 58 | { 59 | int imin = 0; 60 | int imax = vec->count; 61 | 62 | while(imin < imax) { 63 | int imid = (imin + imax) >> 1; 64 | 65 | if(vec->v[imid] < value) 66 | imin = imid + 1; 67 | else 68 | imax = imid; 69 | } 70 | return imin; 71 | } 72 | 73 | 74 | int 75 | intvec_insert_sorted(intvec_t *vec, int value) 76 | { 77 | int position = intvec_search(vec, value); 78 | intvec_insert(vec, position, value); 79 | return position; 80 | } 81 | 82 | 83 | int 84 | intvec_find(const intvec_t *vec, int value) 85 | { 86 | if(vec->count == 0) 87 | return -1; 88 | const int pos = intvec_search(vec, value); 89 | return pos < vec->count && vec->v[pos] == value ? pos : -1; 90 | } 91 | 92 | void 93 | intvec_copy(intvec_t *dst, const intvec_t *src) 94 | { 95 | // We trim the capacity down to the actual size here 96 | dst->count = dst->capacity = src->count; 97 | 98 | dst->v = malloc(dst->count * sizeof(dst->v[0])); 99 | memcpy(dst->v, src->v, dst->count * sizeof(dst->v[0])); 100 | } 101 | 102 | 103 | #ifdef TEST 104 | 105 | static void 106 | printvec(intvec_t *v) 107 | { 108 | for(int i = 0; i < v->count; i++) { 109 | printf("%d ", v->v[i]); 110 | } 111 | printf("\n"); 112 | } 113 | 114 | 115 | int main(void) 116 | { 117 | intvec_t v = {}; 118 | 119 | intvec_insert_sorted(&v, 5); 120 | printvec(&v); 121 | intvec_insert_sorted(&v, 3); 122 | printvec(&v); 123 | intvec_insert_sorted(&v, 7); 124 | printvec(&v); 125 | intvec_insert_sorted(&v, 1); 126 | printvec(&v); 127 | intvec_insert_sorted(&v, 11); 128 | printvec(&v); 129 | intvec_insert_sorted(&v, 7); 130 | printvec(&v); 131 | intvec_insert_sorted(&v, 7); 132 | printvec(&v); 133 | intvec_insert_sorted(&v, 7); 134 | printvec(&v); 135 | intvec_insert_sorted(&v, 7); 136 | printvec(&v); 137 | intvec_insert_sorted(&v, 7); 138 | printvec(&v); 139 | intvec_insert_sorted(&v, 7); 140 | printvec(&v); 141 | intvec_insert_sorted(&v, 7); 142 | printvec(&v); 143 | intvec_insert_sorted(&v, 1); 144 | printvec(&v); 145 | intvec_insert_sorted(&v, 1); 146 | printvec(&v); 147 | intvec_insert_sorted(&v, 1); 148 | printvec(&v); 149 | intvec_insert_sorted(&v, 1); 150 | printvec(&v); 151 | intvec_insert_sorted(&v, 0); 152 | printvec(&v); 153 | intvec_insert_sorted(&v, 11); 154 | printvec(&v); 155 | intvec_insert_sorted(&v, 12); 156 | printvec(&v); 157 | intvec_insert_sorted(&v, 13); 158 | printvec(&v); 159 | 160 | printf("Find %d = %d\n", 0, intvec_find(&v, 0)); 161 | printf("Find %d = %d\n", 7, intvec_find(&v, 7)); 162 | printf("Find %d = %d\n", 8, intvec_find(&v, 8)); 163 | printf("Find %d = %d\n", 12, intvec_find(&v, 12)); 164 | printf("Find %d = %d\n", 1200, intvec_find(&v, 1200)); 165 | printf("Find %d = %d\n", -120, intvec_find(&v, -120)); 166 | intvec_reset(&v); 167 | } 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /intvec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct intvec { 4 | size_t capacity; 5 | size_t count; 6 | int *v; 7 | } intvec_t; 8 | 9 | 10 | void intvec_push(intvec_t *vec, int value); 11 | 12 | void intvec_reset(intvec_t *vec); 13 | 14 | void intvec_insert(intvec_t *vec, int position, int value); 15 | 16 | void intvec_delete(intvec_t *vec, unsigned int position); 17 | 18 | int intvec_insert_sorted(intvec_t *vec, int value); 19 | 20 | int intvec_find(const intvec_t *vec, int value); 21 | 22 | void intvec_copy(intvec_t *dst, const intvec_t *src); 23 | 24 | #define intvec_get(x, i) (x)->v[i] 25 | 26 | #define scoped_intvec(x) intvec_t x __attribute__((cleanup(intvec_reset))) = {} 27 | 28 | -------------------------------------------------------------------------------- /irc.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | void irc_msg_channel(const char *url, const char *channel, 27 | const char *key, const char *message, 28 | int ttl_in_seconds); 29 | -------------------------------------------------------------------------------- /json.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | typedef struct json_deserializer { 27 | void *(*jd_create_map)(void *jd_opaque); 28 | void *(*jd_create_list)(void *jd_opaque); 29 | 30 | void (*jd_destroy_obj)(void *jd_opaque, void *obj); 31 | 32 | void (*jd_add_obj)(void *jd_opaque, void *parent, 33 | const char *name, void *child); 34 | 35 | // str must be free'd by callee 36 | void (*jd_add_string)(void *jd_opaque, void *parent, 37 | const char *name, char *str); 38 | 39 | void (*jd_add_long)(void *jd_opaque, void *parent, 40 | const char *name, long v); 41 | 42 | void (*jd_add_double)(void *jd_opaque, void *parent, 43 | const char *name, double d); 44 | 45 | void (*jd_add_bool)(void *jd_opaque, void *parent, 46 | const char *name, int v); 47 | 48 | void (*jd_add_null)(void *jd_opaque, void *parent, 49 | const char *name); 50 | 51 | void (*jd_add_comment)(void *jd_opaque, void *parent, 52 | const char *comment); 53 | 54 | } json_deserializer_t; 55 | 56 | void *json_deserialize(const char *src, const json_deserializer_t *jd, 57 | void *opaque, char *errbuf, size_t errlen); 58 | -------------------------------------------------------------------------------- /libsvc.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "libsvc.h" 30 | #include "tcp.h" 31 | #include "misc.h" 32 | #include "init.h" 33 | #include "trace.h" 34 | #include "vec.h" 35 | 36 | #ifdef WITH_OPENSSL 37 | #include 38 | #include 39 | #include 40 | #endif 41 | 42 | #ifdef WITH_MYSQL 43 | #include "db.h" 44 | #endif 45 | 46 | #ifdef WITH_ASYNCIO 47 | #include "asyncio.h" 48 | #endif 49 | 50 | #ifdef WITH_CURL 51 | #include 52 | #endif 53 | 54 | 55 | typedef struct { 56 | void (*init)(void); 57 | void (*term)(void); 58 | void (*fini)(void); 59 | int prio; 60 | } inithelper_t; 61 | 62 | static VEC_HEAD(, inithelper_t) inithelpers; 63 | 64 | /** 65 | * 66 | */ 67 | static int 68 | ihcmp(const inithelper_t *a, const inithelper_t *b) 69 | { 70 | return a->prio - b->prio; 71 | } 72 | 73 | 74 | void 75 | inithelper_register(void (*init)(void), void (*term)(void), 76 | void (*fini)(void), int prio) 77 | { 78 | VEC_PUSH_BACK(&inithelpers, ((const inithelper_t) { 79 | .init = init, .term = term, .fini = fini, .prio = prio})); 80 | } 81 | 82 | 83 | char *libsvc_app_version; 84 | char *libsvc_app_version_only; 85 | 86 | 87 | void 88 | libsvc_set_app_version(const char *version) 89 | { 90 | libsvc_app_version = fmt("%s-%s", PROGNAME, version); 91 | libsvc_app_version_only = strdup(version); 92 | } 93 | 94 | 95 | 96 | 97 | void 98 | libsvc_init(void) 99 | { 100 | #ifdef WITH_MYSQL 101 | db_init(); 102 | #endif 103 | 104 | #ifdef WITH_ASYNCIO 105 | asyncio_init(); 106 | #endif 107 | 108 | tcp_init(NULL); 109 | 110 | #ifdef WITH_CURL 111 | curl_global_init(CURL_GLOBAL_ALL); 112 | #endif 113 | 114 | #ifdef WITH_TCP_SERVER 115 | tcp_server_init(); 116 | #endif 117 | 118 | VEC_SORT(&inithelpers, ihcmp); 119 | for(ssize_t i = 0; i < VEC_LEN(&inithelpers); i++) { 120 | if(VEC_ITEM(&inithelpers, i).init) 121 | VEC_ITEM(&inithelpers, i).init(); 122 | } 123 | } 124 | 125 | 126 | void 127 | libsvc_fini(void) 128 | { 129 | for(ssize_t i = VEC_LEN(&inithelpers) - 1; i >= 0; i--) { 130 | if(VEC_ITEM(&inithelpers, i).fini) 131 | VEC_ITEM(&inithelpers, i).fini(); 132 | } 133 | } 134 | 135 | void 136 | libsvc_term(void) 137 | { 138 | for(ssize_t i = VEC_LEN(&inithelpers) - 1; i >= 0; i--) { 139 | if(VEC_ITEM(&inithelpers, i).term) 140 | VEC_ITEM(&inithelpers, i).term(); 141 | } 142 | } 143 | 144 | 145 | void 146 | libsvc_set_fdlimit(int num_fd) 147 | { 148 | struct rlimit lim; 149 | 150 | 151 | if(getrlimit(RLIMIT_NOFILE, &lim) == -1) { 152 | trace(LOG_ERR, "Unable to get fdlimit -- %s", 153 | strerror(errno)); 154 | } 155 | 156 | lim.rlim_cur = num_fd < lim.rlim_max ? num_fd : lim.rlim_max; 157 | 158 | if(setrlimit(RLIMIT_NOFILE, &lim) == -1) { 159 | trace(LOG_ERR, "Unable to set fdlimit to %d -- %s", 160 | num_fd, strerror(errno)); 161 | trace(LOG_INFO, "Current fdlimit %ld (max: %ld)", 162 | (long)lim.rlim_cur, (long)lim.rlim_max); 163 | exit(1); 164 | } 165 | 166 | trace(LOG_INFO, "Current fdlimit %ld (max: %ld)", 167 | (long)lim.rlim_cur, (long)lim.rlim_max); 168 | 169 | } 170 | 171 | #if defined(WITH_OPENSSL) 172 | 173 | #if OPENSSL_VERSION_NUMBER < 0x10100000 174 | static pthread_mutex_t *ssl_locks; 175 | 176 | /** 177 | * 178 | */ 179 | static unsigned long __attribute__((unused)) 180 | ssl_tid_fn(void) 181 | { 182 | return (unsigned long)pthread_self(); 183 | } 184 | 185 | static void __attribute__((unused)) 186 | ssl_lock_fn(int mode, int n, const char *file, int line) 187 | { 188 | if(mode & CRYPTO_LOCK) 189 | pthread_mutex_lock(&ssl_locks[n]); 190 | else if(mode & CRYPTO_UNLOCK) 191 | pthread_mutex_unlock(&ssl_locks[n]); 192 | } 193 | #endif 194 | 195 | static pthread_once_t once_openssl = PTHREAD_ONCE_INIT; 196 | 197 | 198 | static void 199 | init_openssl(void) 200 | { 201 | #if OPENSSL_VERSION_NUMBER < 0x10100000 202 | 203 | SSL_library_init(); 204 | SSL_load_error_strings(); 205 | 206 | int i, n = CRYPTO_num_locks(); 207 | ssl_locks = malloc_mul(sizeof(pthread_mutex_t), n); 208 | for(i = 0; i < n; i++) 209 | pthread_mutex_init(&ssl_locks[i], NULL); 210 | 211 | CRYPTO_set_locking_callback(ssl_lock_fn); 212 | CRYPTO_set_id_callback(ssl_tid_fn); 213 | #endif 214 | 215 | uint8_t randomness[32]; 216 | get_random_bytes(randomness, sizeof(randomness)); 217 | RAND_seed(randomness, sizeof(randomness)); 218 | } 219 | 220 | 221 | void 222 | libsvc_openssl_init(void) 223 | { 224 | pthread_once(&once_openssl, init_openssl); 225 | } 226 | 227 | #else 228 | 229 | void 230 | libsvc_openssl_init(void) 231 | { 232 | } 233 | #endif 234 | 235 | -------------------------------------------------------------------------------- /libsvc.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | void libsvc_init(void); 25 | 26 | void libsvc_term(void); 27 | 28 | void libsvc_fini(void); 29 | 30 | void libsvc_set_fdlimit(int num_fd); 31 | 32 | void libsvc_set_app_version(const char *version); 33 | 34 | void libsvc_openssl_init(void); 35 | -------------------------------------------------------------------------------- /libsvc.mk: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := $(if $(GOAL),$(GOAL),$(PROG)) 2 | 3 | ifdef V 4 | cmd = $2 5 | else 6 | cmd = @echo "$1";$2 7 | endif 8 | 9 | prefix ?= /usr/local 10 | 11 | CSTANDARD ?= gnu99 12 | 13 | CFLAGS += -Wall -Werror -Wwrite-strings -Wno-deprecated-declarations 14 | CFLAGS += -Wmissing-prototypes -std=${CSTANDARD} -DPROGNAME=\"${PROGNAME}\" 15 | 16 | include libsvc/sources.mk 17 | 18 | SRCS += ${libsvc_SRCS:%.c=libsvc/%.c} 19 | 20 | ############################################################## 21 | # Final linker stuff 22 | ############################################################## 23 | 24 | LDFLAGS += -lssl -lcrypto -lpthread -lm 25 | 26 | ifeq ($(shell uname),Linux) 27 | LDFLAGS += -lrt 28 | endif 29 | 30 | ############################################################## 31 | 32 | ALLDEPS += libsvc/libsvc.mk Makefile libsvc/sources.mk 33 | 34 | OBJS += $(SRCS:%.c=$(BUILDDIR)/%.o) 35 | OBJS := $(OBJS:%.cc=$(BUILDDIR)/%.o) 36 | OBJS := $(OBJS:%.cpp=$(BUILDDIR)/%.o) 37 | 38 | DEPS += ${OBJS:%.o=%.d} 39 | 40 | # Common CFLAGS for all files 41 | CFLAGS_com += -g -funsigned-char -D_FILE_OFFSET_BITS=64 42 | CFLAGS_com += -I${BUILDDIR} -I${CURDIR} 43 | CFLAGS_opt ?= -O2 44 | 45 | 46 | $(BUILDDIR)/bundles/%.o: $(BUILDDIR)/bundles/%.c $(ALLDEPS) 47 | $(call cmd,Compiling $@,\ 48 | $(CC) ${CFLAGS} ${CFLAGS_com} ${CFLAGS_opt} -c -o $@ $<) 49 | 50 | $(BUILDDIR)/bundles/%.c: % $(CURDIR)/libsvc/mkbundle $(ALLDEPS) 51 | @mkdir -p $(dir $@) 52 | $(call cmd,Creating bundle $@,\ 53 | $(MKBUNDLE) -o $@ -s $< -d ${BUILDDIR}/bundles/$<.d -p $<) 54 | 55 | $(BUILDDIR)/zbundles/%.o: $(BUILDDIR)/zbundles/%.c $(ALLDEPS) 56 | $(call cmd,Compiling $@,\ 57 | $(CC) ${CFLAGS} ${CFLAGS_com} ${CFLAGS_opt} -c -o $@ $<) 58 | 59 | $(BUILDDIR)/zbundles/%.c: % $(CURDIR)/libsvc/mkbundle $(ALLDEPS) 60 | @mkdir -p $(dir $@) 61 | $(call cmd,Creating bundle $@,\ 62 | $(MKBUNDLE) -z -o $@ -s $< -d ${BUILDDIR}/zbundles/$<.d -p $<) 63 | 64 | 65 | # File bundles 66 | BUNDLES += $(sort $(BUNDLES-yes)) 67 | BUNDLE_SRCS=$(BUNDLES:%=$(BUILDDIR)/bundles/%.c) 68 | 69 | ZBUNDLES += $(sort $(ZBUNDLES-yes)) 70 | ZBUNDLE_SRCS=$(ZBUNDLES:%=$(BUILDDIR)/zbundles/%.c) 71 | 72 | DEPS += $(BUNDLE_SRCS:%.c=%.d) $(ZBUNDLE_SRCS:%.c=%.d) 73 | BUNDLE_OBJS += $(BUNDLE_SRCS:%.c=%.o) $(ZBUNDLE_SRCS:%.c=%.o) 74 | .PRECIOUS: ${BUNDLE_SRCS} ${ZBUNDLE_SRCS} 75 | 76 | 77 | MKBUNDLE = $(CURDIR)/libsvc/mkbundle 78 | 79 | all: ${PROG} ${PROG}.installable 80 | 81 | ${PROG}: $(OBJS) $(ALLDEPS) ${BUILDDIR}/libsvc/filebundle_disk.o 82 | @mkdir -p $(dir $@) 83 | $(call cmd,Linking $@,\ 84 | $(CC) -o $@ $(OBJS) ${BUILDDIR}/libsvc/filebundle_disk.o $(LDFLAGS) ${LDFLAGS_cfg}) 85 | 86 | ${PROG}.installable: $(OBJS) $(BUNDLE_OBJS) $(ALLDEPS) ${BUILDDIR}/libsvc/filebundle_embedded.o 87 | @mkdir -p $(dir $@) 88 | $(call cmd,Linking $@,\ 89 | $(CC) -o $@ $(OBJS) $(BUNDLE_OBJS) ${BUILDDIR}/libsvc/filebundle_embedded.o $(LDFLAGS) ${LDFLAGS_cfg}) 90 | 91 | ${BUILDDIR}/%.o: %.c $(ALLDEPS) 92 | @mkdir -p $(dir $@) 93 | $(call cmd,Compiling $<,\ 94 | $(CC) -MD -MP $(CFLAGS_com) $(CFLAGS) ${CFLAGS_opt} -c -o $@ $(CURDIR)/$<) 95 | 96 | ${BUILDDIR}/%.o: %.cc $(ALLDEPS) 97 | @mkdir -p $(dir $@) 98 | $(call cmd,Compiling $<,\ 99 | $(CXX) -MD -MP $(CXXFLAGS_com) $(CXXFLAGS) ${CXXFLAGS_opt} -c -o $@ $(CURDIR)/$<) 100 | 101 | ${BUILDDIR}/%.o: %.cpp $(ALLDEPS) 102 | @mkdir -p $(dir $@) 103 | $(call cmd,Compiling $<,\ 104 | $(CXX) -MD -MP $(CXXFLAGS_com) $(CXXFLAGS) ${CXXFLAGS_opt} -c -o $@ $(CURDIR)/$<) 105 | 106 | .PHONY: clean distclean 107 | 108 | clean: 109 | rm -rf ${BUILDDIR}/src 110 | find . -name "*~" -print0 | xargs -0 rm -f 111 | 112 | distclean: clean 113 | rm -rf ${BUILDDIR} 114 | 115 | -------------------------------------------------------------------------------- /mbuf.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define MBUF_DEFAULT_DATA_SIZE 4096 33 | 34 | TAILQ_HEAD(mbuf_data_queue, mbuf_data); 35 | 36 | 37 | typedef enum { 38 | // mbuf_data represents malloced memeory 39 | MBUF_MALLOC, 40 | 41 | // mbuf_data doesn't represent any data at all but rather when 42 | // this buffer is consumed the callback is invoked instead 43 | MBUF_CALLBACK 44 | } mbuf_data_type_t; 45 | 46 | 47 | typedef struct mbuf_data { 48 | TAILQ_ENTRY(mbuf_data) md_link; 49 | 50 | size_t md_data_len; /* Number of valid bytes from md_data */ 51 | size_t md_data_off; /* Offset in data, used for partial reads */ 52 | 53 | int md_flags; 54 | mbuf_data_type_t md_type; 55 | 56 | union { 57 | 58 | struct { 59 | uint8_t *md_data; 60 | size_t md_data_size; /* Size of allocation hb_data */ 61 | }; 62 | 63 | struct { 64 | void (*md_callback)(void *opaque); 65 | void *md_opaque; 66 | }; 67 | }; 68 | 69 | } mbuf_data_t; 70 | 71 | 72 | 73 | #define MBUF_SOM 0x1 /* Start-of-message */ 74 | 75 | typedef struct mbuf { 76 | struct mbuf_data_queue mq_buffers; 77 | size_t mq_size; 78 | size_t mq_alloc_size; 79 | } mbuf_t; 80 | 81 | #define MBUF_INITIALIZER(m) \ 82 | { { NULL, &(m).mq_buffers.tqh_first }, 0, MBUF_DEFAULT_DATA_SIZE } 83 | 84 | void mbuf_data_free(mbuf_t *mq, mbuf_data_t *md); 85 | 86 | void mbuf_init(mbuf_t *m); 87 | 88 | void mbuf_set_chunk_size(mbuf_t *m, size_t s); 89 | 90 | void mbuf_clear(mbuf_t *m); 91 | 92 | #define scoped_mbuf_t mbuf_t __attribute__((cleanup(mbuf_clear))) 93 | 94 | void mbuf_vqprintf(mbuf_t *m, const char *fmt, va_list ap); 95 | 96 | void mbuf_qprintf(mbuf_t *m, const char *fmt, ...) 97 | __attribute__ ((format (printf, 2, 3))); 98 | 99 | void mbuf_append(mbuf_t *m, const void *buf, size_t len); 100 | 101 | // Set start-of-message flag and append 102 | void mbuf_append_som(mbuf_t *mq, const void *buf, size_t len); 103 | 104 | void mbuf_append_str(mbuf_t *m, const char *buf); 105 | 106 | void mbuf_append_prealloc(mbuf_t *m, void *buf, size_t len); 107 | 108 | void mbuf_append_callback(mbuf_t *mq, void (*cb)(void *opaque), void *opaque); 109 | 110 | void mbuf_append_FILE(mbuf_t *m, FILE *fp); 111 | 112 | void mbuf_prepend(mbuf_t *m, const void *buf, size_t len); 113 | 114 | size_t mbuf_read(mbuf_t *m, void *buf, size_t len); 115 | 116 | size_t mbuf_peek(mbuf_t *m, void *buf, size_t len); 117 | 118 | size_t mbuf_peek_no_copy(mbuf_t *mq, const void **buf); 119 | 120 | size_t mbuf_peek_tail(mbuf_t *mq, void *buf, size_t len); 121 | 122 | size_t mbuf_drop(mbuf_t *m, size_t len); 123 | 124 | size_t mbuf_drop_tail(mbuf_t *mq, size_t len); 125 | 126 | int mbuf_find(mbuf_t *m, uint8_t v); 127 | 128 | void mbuf_appendq(mbuf_t *m, mbuf_t *src); 129 | 130 | // Write mbuf to file 131 | int mbuf_write_FILE(mbuf_t *mq, FILE* fp); 132 | 133 | void mbuf_prependq(mbuf_t *mq, mbuf_t *src); 134 | 135 | void mbuf_copyq(mbuf_t *mq, const mbuf_t *src); 136 | 137 | void mbuf_append_and_escape_xml(mbuf_t *m, const char *str); 138 | 139 | void mbuf_append_and_escape_url(mbuf_t *m, const char *s); 140 | 141 | void mbuf_append_and_escape_jsonstr(mbuf_t *m, const char *s, 142 | int escape_slash); 143 | 144 | void mbuf_append_u8(mbuf_t *m, uint8_t u8); 145 | 146 | void mbuf_append_u16_be(mbuf_t *m, uint16_t u16); 147 | 148 | void mbuf_append_u32_be(mbuf_t *m, uint32_t u32); 149 | 150 | void mbuf_dump_raw_stderr(mbuf_t *m); 151 | 152 | void mbuf_hexdump(const char *prefix, mbuf_t *mq); 153 | 154 | const void *mbuf_pullup(mbuf_t *mq, size_t bytes); 155 | 156 | char *mbuf_clear_to_string(mbuf_t *mq) 157 | __attribute__((warn_unused_result)); 158 | 159 | int mbuf_deflate(mbuf_t *dst, mbuf_t *src, int level) 160 | __attribute__((warn_unused_result)); 161 | 162 | int mbuf_gzip(mbuf_t *dst, mbuf_t *src, int level) 163 | __attribute__((warn_unused_result)); 164 | 165 | 166 | /** 167 | * Group of queues for prioritized packet scheduling 168 | */ 169 | 170 | typedef struct mbuf_grp mbuf_grp_t; 171 | 172 | typedef enum { 173 | MBUF_GRP_MODE_STRICT_PRIORITY = 0, 174 | } mbuf_grp_mode_t; 175 | 176 | mbuf_grp_t *mbuf_grp_create(mbuf_grp_mode_t mode); 177 | 178 | void mbuf_grp_destroy(mbuf_grp_t *mg); 179 | 180 | void mbuf_grp_append(mbuf_grp_t *mg, int queue, 181 | const void *data, size_t len, int start_of_message); 182 | 183 | void mbuf_grp_appendq(mbuf_grp_t *mg, int queue, mbuf_t *src); 184 | 185 | size_t mbuf_grp_peek_no_copy(mbuf_grp_t *mg, const void **buf); 186 | 187 | void mbuf_grp_drop(mbuf_grp_t *mg, size_t size); 188 | 189 | size_t mbuf_grp_size(mbuf_grp_t *mg); 190 | 191 | size_t mbuf_grp_size_for_queue(mbuf_grp_t *mg, int queue_index); 192 | -------------------------------------------------------------------------------- /memstream.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #define _GNU_SOURCE 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "memstream.h" 31 | 32 | #ifdef linux 33 | 34 | FILE * 35 | open_buffer(char **out, size_t *outlen) 36 | { 37 | return open_memstream(out, outlen); 38 | } 39 | 40 | #else 41 | 42 | /** 43 | * 44 | */ 45 | typedef struct bufhelper { 46 | char **out; 47 | size_t *outlen; 48 | } bufhelper_t; 49 | 50 | 51 | /** 52 | * 53 | */ 54 | static int 55 | buf_write(void *aux, const char *data, int len) 56 | { 57 | bufhelper_t *bh = aux; 58 | int needlen = *bh->outlen + len; 59 | *bh->out = realloc(*bh->out, needlen); 60 | memcpy(*bh->out + *bh->outlen, data, len); 61 | *bh->outlen = needlen; 62 | return len; 63 | } 64 | 65 | /** 66 | * 67 | */ 68 | static int 69 | buf_close(void *aux) 70 | { 71 | free(aux); 72 | return 0; 73 | } 74 | 75 | /** 76 | * 77 | */ 78 | FILE * 79 | open_buffer(char **out, size_t *outlen) 80 | { 81 | *outlen = 0; 82 | *out = NULL; 83 | bufhelper_t *bh = malloc(sizeof(bufhelper_t)); 84 | bh->out = out; 85 | bh->outlen = outlen; 86 | return funopen(bh, NULL, buf_write, NULL, buf_close); 87 | } 88 | 89 | #endif 90 | 91 | 92 | typedef struct readhelper { 93 | const char *data; 94 | size_t size; 95 | off_t pos; 96 | } readhelper_t; 97 | 98 | #ifdef __APPLE__ 99 | 100 | /** 101 | * 102 | */ 103 | static int 104 | buf_read(void *aux, char *data, int len) 105 | { 106 | readhelper_t *rh = aux; 107 | 108 | if(rh->pos + len > rh->size) 109 | len = rh->size - rh->pos; 110 | 111 | memcpy(data, rh->data + rh->pos, len); 112 | rh->pos += len; 113 | return len; 114 | } 115 | 116 | 117 | /** 118 | * 119 | */ 120 | static fpos_t 121 | buf_seek(void *aux, fpos_t offset, int whence) 122 | { 123 | readhelper_t *rh = aux; 124 | switch(whence) { 125 | case SEEK_SET: 126 | rh->pos = offset; 127 | break; 128 | case SEEK_CUR: 129 | rh->pos += offset; 130 | break; 131 | case SEEK_END: 132 | rh->pos = rh->size; 133 | break; 134 | } 135 | return rh->pos; 136 | } 137 | 138 | #else 139 | 140 | static ssize_t 141 | buf_read(void *fh, char *data, size_t len) 142 | { 143 | readhelper_t *rh = fh; 144 | 145 | if(rh->pos + len > rh->size) 146 | len = rh->size - rh->pos; 147 | 148 | memcpy(data, rh->data + rh->pos, len); 149 | rh->pos += len; 150 | return len; 151 | } 152 | 153 | 154 | /** 155 | * 156 | */ 157 | static int 158 | buf_seek(void *fh, off64_t *offsetp, int whence) 159 | { 160 | readhelper_t *rh = fh; 161 | switch(whence) { 162 | case SEEK_SET: 163 | rh->pos = *offsetp; 164 | break; 165 | case SEEK_CUR: 166 | rh->pos += *offsetp; 167 | break; 168 | case SEEK_END: 169 | rh->pos = rh->size + *offsetp; 170 | return -1; 171 | } 172 | *offsetp = rh->pos; 173 | return 0; 174 | } 175 | 176 | /** 177 | * 178 | */ 179 | static int 180 | buf_close(void *aux) 181 | { 182 | free(aux); 183 | return 0; 184 | } 185 | 186 | 187 | static cookie_io_functions_t read_functions = { 188 | .read = buf_read, 189 | .seek = buf_seek, 190 | .close = buf_close, 191 | }; 192 | 193 | 194 | #endif 195 | 196 | FILE * 197 | open_buffer_read(const void *buf, size_t len) 198 | { 199 | readhelper_t *rh = malloc(sizeof(readhelper_t)); 200 | rh->data = buf; 201 | rh->size = len; 202 | rh->pos = 0; 203 | #ifdef __APPLE__ 204 | return funopen(rh, buf_read, NULL, buf_seek, buf_close); 205 | #else 206 | return fopencookie(rh, "rb", read_functions); 207 | #endif 208 | } 209 | 210 | 211 | -------------------------------------------------------------------------------- /memstream.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | FILE *open_buffer(char **out, size_t *outlen); 29 | 30 | FILE *open_buffer_read(const void *buf, size_t len); 31 | -------------------------------------------------------------------------------- /misc.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define URL_ESCAPE_PATH 1 33 | #define URL_ESCAPE_PARAM 2 34 | 35 | int url_escape(char *dst, const int size, const char *src, int how); 36 | 37 | char *url_escape_tmp(const char *src, int how); 38 | 39 | char *url_escape_alloc(const char *src, int how); 40 | 41 | #define BASE64_STANDARD 0 42 | #define BASE64_URL 1 43 | 44 | int base64_encode(char *out, int out_size, const void *in, int in_size); 45 | 46 | int base64_decode(uint8_t *out, const char *in, int out_size); 47 | 48 | #define BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) 49 | 50 | char *base64_encode_a(const void *in, int in_size, int mode); 51 | 52 | int dictcmp(const char *a, const char *b); 53 | 54 | #define WRITEFILE_NO_CHANGE 1000000 55 | 56 | int writefile(const char *path, const void *buf, int size, int checksame); 57 | 58 | char *readfile(const char *path, time_t *ts); 59 | 60 | int mkdir_p(const char *path, int mode); 61 | 62 | int mkdir_chown_p(const char *path, uid_t uid, uid_t gid, int mode); 63 | 64 | int rm_rf(const char *path, int remove_self); 65 | 66 | void get_random_bytes(void *out, size_t len); 67 | 68 | typedef struct { uint32_t a; uint32_t b; uint32_t c; uint32_t d; } prng_t; 69 | 70 | uint32_t prng_get(prng_t *x); 71 | 72 | void prng_init(prng_t *x); 73 | 74 | 75 | #define mystrdupa(n) ({ int my_l = strlen(n); \ 76 | char *my_b = alloca(my_l + 1); \ 77 | memcpy(my_b, n, my_l + 1); }) 78 | 79 | #define mystrndupa(n, len) ({ \ 80 | char *my_b = alloca(len + 1); \ 81 | my_b[len] = 0; \ 82 | memcpy(my_b, n, len); \ 83 | }) 84 | 85 | // Check if s1 begins with s2 86 | static inline const char *mystrbegins(const char *s1, const char *s2) 87 | { 88 | while(*s2) 89 | if(*s1++ != *s2++) 90 | return NULL; 91 | return s1; 92 | } 93 | 94 | 95 | int64_t get_ts(void); 96 | 97 | int64_t get_ts_mono(void); 98 | 99 | void strset(char **p, const char *s); 100 | 101 | int str_tokenize(char *buf, char **vec, int vecsize, int delimiter); 102 | 103 | int hexnibble(char c); 104 | 105 | int hex2bin(uint8_t *buf, size_t buflen, const char *str); 106 | 107 | void bin2hex(char *dst, size_t dstlen, const uint8_t *src, size_t srclen); 108 | 109 | char *bin2str(const void *src, size_t len); 110 | 111 | const char *time_to_RFC_1123(time_t t); 112 | #if 0 113 | void strvec_addp(char ***str, const char *v); 114 | 115 | void strvec_addpn(char ***str, const char *v, size_t len); 116 | 117 | char **strvec_split(const char *str, char ch); 118 | 119 | void strvec_free(char **s); 120 | 121 | int strvec_len(char **s); 122 | 123 | char **strvec_dup(char **s); 124 | #endif 125 | 126 | void http_deescape(char *s); 127 | 128 | char *lp_get(char **lp); 129 | 130 | #define LINEPARSE(out, src) for(char *lp = src, *out; (out = lp_get(&lp)) != NULL; ) 131 | 132 | #ifndef ARRAYSIZE 133 | #define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0])) 134 | #endif 135 | 136 | size_t html_enteties_escape(const char *src, char *dst); 137 | 138 | const char *html_enteties_escape_tmp(const char *src); 139 | 140 | char * str_replace_tokens(char *str, const char *tokenprefix, 141 | const char *tokenpostfix, 142 | const char **tokens); 143 | 144 | void freecharp(char **ptr); 145 | 146 | void freeuint8p(uint8_t **ptr); 147 | 148 | #define scoped_char char __attribute__((cleanup(freecharp))) 149 | 150 | #define scoped_uint8_t uint8_t __attribute__((cleanup(freeuint8p))) 151 | 152 | char *fmtv(const char *fmt, va_list ap); 153 | 154 | char *fmt(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 155 | 156 | void *malloc_add(size_t a, size_t b); 157 | 158 | void *malloc_mul(size_t a, size_t b); 159 | 160 | int64_t rfc3339_date_parse(const char *s, int roundup); 161 | -------------------------------------------------------------------------------- /mkbundle: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Filebundle generator 4 | # Copyright (C) 2009 Andreas Öman 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | set -u # Fail on undefined vars 21 | 22 | READER="cat" 23 | SOURCE= 24 | DEPFILE= 25 | OUTPUT= 26 | PREFIX= 27 | COMPRESS=false 28 | usage() 29 | { 30 | cat << EOF 31 | usage: $0 options 32 | 33 | Generate a filebundle .c-file from a directory 34 | 35 | OPTIONS: 36 | -h Show this message 37 | -s Source path 38 | -d Dependency file (for use with make) 39 | -o Output file (.c file) 40 | -z Compress individual files using gzip 41 | -p Filebundle prefix 42 | EOF 43 | } 44 | 45 | 46 | while getopts "s:d:o:zhp:" OPTION 47 | do 48 | case $OPTION in 49 | h) 50 | usage 51 | exit 1 52 | ;; 53 | z) 54 | READER="gzip -9" 55 | COMPRESS=true 56 | ;; 57 | d) 58 | DEPFILE="$OPTARG" 59 | ;; 60 | s) 61 | SOURCE="$OPTARG" 62 | ;; 63 | o) 64 | OUTPUT="$OPTARG" 65 | ;; 66 | p) 67 | PREFIX="$OPTARG" 68 | ;; 69 | esac 70 | done 71 | 72 | 73 | if [[ -z $SOURCE ]] || [[ -z $OUTPUT ]] || [[ -z $PREFIX ]]; then 74 | usage 75 | exit 1 76 | fi 77 | 78 | FILES=`find "${SOURCE}" -mindepth 1 -and \( \( -name .svn -or -name "*~" -or -name .DS_Store \) -and -prune \) -or -print | sed "s%^$SOURCE/%%" | xargs echo` 79 | 80 | echo >${OUTPUT} "// auto-generated by $0" 81 | 82 | for file in $FILES; do 83 | 84 | if [ -f ${SOURCE}/$file ]; then 85 | 86 | name=`echo $file | sed -e s#[/.-]#_#g` 87 | 88 | echo >>${OUTPUT} "// ${SOURCE}/$file" 89 | echo >>${OUTPUT} "static const unsigned char embedded_$name[]={" 90 | $READER <${SOURCE}/$file | od -v -An -b | awk '/[^\s]+/ { print $0 }' | sed s/^\ */0/ | sed s/\ *$/,/| sed s/\ /,\ 0/g >>${OUTPUT} 91 | echo >>${OUTPUT} "};" 92 | fi 93 | done 94 | 95 | echo >>${OUTPUT} "#include \"libsvc/filebundle.h\"" 96 | echo >>${OUTPUT} "static const struct filebundle_entry filebundle_entries[] = {" 97 | [[ -z $DEPFILE ]] || echo >${DEPFILE} -n "${OUTPUT}: ${SOURCE} " 98 | 99 | for file in $FILES; do 100 | [[ -z $DEPFILE ]] || echo >>${DEPFILE} -n "${SOURCE}/$file " 101 | 102 | if [ -f ${SOURCE}/$file ]; then 103 | 104 | if $COMPRESS; then 105 | ORIGINAL_SIZE=`stat -c "%s" ${SOURCE}/$file` 106 | else 107 | ORIGINAL_SIZE="-1" 108 | fi 109 | 110 | N=`echo $file | sed -e s#[/.-]#_#g` 111 | echo >>${OUTPUT} "{\"$file\", embedded_$N, sizeof(embedded_$N),${ORIGINAL_SIZE}}," 112 | fi 113 | done 114 | 115 | echo >>${OUTPUT} "{(void *)0, 0, 0, 0}};" 116 | [[ -z $DEPFILE ]] || echo >>${DEPFILE} "" 117 | 118 | for file in $FILES; do 119 | [[ -z $DEPFILE ]] || echo >>${DEPFILE} "${SOURCE}/$file: " 120 | done 121 | 122 | cat >>${OUTPUT} < 22 | 23 | #define ROTL32(x,y) _rotl(x,y) 24 | #define ROTL64(x,y) _rotl64(x,y) 25 | 26 | #define BIG_CONSTANT(x) (x) 27 | 28 | // Other compilers 29 | 30 | #else // defined(_MSC_VER) 31 | 32 | #define FORCE_INLINE inline __attribute__((always_inline)) 33 | 34 | static inline uint32_t rotl32 ( uint32_t x, int8_t r ) 35 | { 36 | return (x << r) | (x >> (32 - r)); 37 | } 38 | 39 | 40 | #define ROTL32(x,y) rotl32(x,y) 41 | 42 | #define BIG_CONSTANT(x) (x##LLU) 43 | 44 | #endif // !defined(_MSC_VER) 45 | 46 | //----------------------------------------------------------------------------- 47 | // Block read - if your platform needs to do endian-swapping or can only 48 | // handle aligned reads, do the conversion here 49 | 50 | static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) 51 | { 52 | return p[i]; 53 | } 54 | 55 | 56 | //----------------------------------------------------------------------------- 57 | // Finalization mix - force all bits of a hash block to avalanche 58 | 59 | static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) 60 | { 61 | h ^= h >> 16; 62 | h *= 0x85ebca6b; 63 | h ^= h >> 13; 64 | h *= 0xc2b2ae35; 65 | h ^= h >> 16; 66 | 67 | return h; 68 | } 69 | 70 | //----------------------------------------------------------------------------- 71 | 72 | uint32_t MurHash3_32(const void * key, int len, uint32_t seed) 73 | { 74 | const uint8_t * data = (const uint8_t*)key; 75 | const int nblocks = len / 4; 76 | 77 | uint32_t h1 = seed; 78 | 79 | const uint32_t c1 = 0xcc9e2d51; 80 | const uint32_t c2 = 0x1b873593; 81 | 82 | //---------- 83 | // body 84 | 85 | const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); 86 | 87 | for(int i = -nblocks; i; i++) 88 | { 89 | uint32_t k1 = getblock32(blocks,i); 90 | 91 | k1 *= c1; 92 | k1 = ROTL32(k1,15); 93 | k1 *= c2; 94 | 95 | h1 ^= k1; 96 | h1 = ROTL32(h1,13); 97 | h1 = h1*5+0xe6546b64; 98 | } 99 | 100 | //---------- 101 | // tail 102 | 103 | const uint8_t * tail = (const uint8_t*)(data + nblocks*4); 104 | 105 | uint32_t k1 = 0; 106 | 107 | switch(len & 3) 108 | { 109 | case 3: k1 ^= tail[2] << 16; 110 | case 2: k1 ^= tail[1] << 8; 111 | case 1: k1 ^= tail[0]; 112 | k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; 113 | }; 114 | 115 | //---------- 116 | // finalization 117 | 118 | h1 ^= len; 119 | 120 | h1 = fmix32(h1); 121 | 122 | return h1; 123 | } 124 | -------------------------------------------------------------------------------- /murmur3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //----------------------------------------------------------------------------- 4 | // MurmurHash3 was written by Austin Appleby, and is placed in the public 5 | // domain. The author hereby disclaims copyright to this source code. 6 | 7 | 8 | //----------------------------------------------------------------------------- 9 | // Platform-specific functions and macros 10 | 11 | // Microsoft Visual Studio 12 | 13 | #if defined(_MSC_VER) && (_MSC_VER < 1600) 14 | 15 | typedef unsigned char uint8_t; 16 | typedef unsigned int uint32_t; 17 | typedef unsigned __int64 uint64_t; 18 | 19 | // Other compilers 20 | 21 | #else // defined(_MSC_VER) 22 | 23 | #include 24 | 25 | #endif // !defined(_MSC_VER) 26 | 27 | uint32_t MurHash3_32(const void * key, int len, uint32_t seed); 28 | -------------------------------------------------------------------------------- /ntv_json.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2016 Andreas Smas 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "ntv.h" 30 | #include "mbuf.h" 31 | #include "dbl.h" 32 | #include "json.h" 33 | 34 | 35 | static void 36 | ntv_json_write_value(const ntv_t *f, mbuf_t *m, int indent, int flags, 37 | int precision); 38 | 39 | /** 40 | * 41 | */ 42 | static void 43 | ntv_json_write(const ntv_t *msg, mbuf_t *m, int indent, int flags, 44 | int precision) 45 | { 46 | ntv_t *f; 47 | const bool isarray = msg->ntv_type == NTV_LIST; 48 | const int escape_slash = !(flags & NTV_JSON_F_MINIMAL_ESCAPE); 49 | 50 | 51 | mbuf_append(m, isarray ? "[" : "{", 1); 52 | indent++; 53 | 54 | TAILQ_FOREACH(f, &msg->ntv_children, ntv_link) { 55 | 56 | if(flags & NTV_JSON_F_PRETTY) { 57 | const int spc = (flags & NTV_JSON_F_WIDE ? 3 : 1) * indent; 58 | mbuf_qprintf(m, "\n%*.s", spc, ""); 59 | } 60 | 61 | if(!isarray) { 62 | mbuf_append_and_escape_jsonstr(m, f->ntv_name ?: "noname", escape_slash); 63 | mbuf_append(m, ": ", flags & NTV_JSON_F_PRETTY ? 2 : 1); 64 | } 65 | 66 | ntv_json_write_value(f, m, indent, flags, precision); 67 | 68 | if(TAILQ_NEXT(f, ntv_link)) 69 | mbuf_append(m, ",", 1); 70 | } 71 | 72 | indent--; 73 | if(flags & NTV_JSON_F_PRETTY) { 74 | const int spc = (flags & NTV_JSON_F_WIDE ? 3 : 1) * indent; 75 | mbuf_qprintf(m, "\n%*.s", spc, ""); 76 | } 77 | mbuf_append(m, isarray ? "]" : "}", 1); 78 | } 79 | 80 | 81 | static void 82 | ntv_json_write_value(const ntv_t *f, mbuf_t *m, int indent, int flags, 83 | int precision) 84 | { 85 | char buf[100]; 86 | const int escape_slash = !(flags & NTV_JSON_F_MINIMAL_ESCAPE); 87 | 88 | switch(f->ntv_type) { 89 | case NTV_MAP: 90 | ntv_json_write(f, m, indent, flags, precision); 91 | break; 92 | 93 | case NTV_LIST: 94 | ntv_json_write(f, m, indent, flags, precision); 95 | break; 96 | 97 | case NTV_STRING: 98 | mbuf_append_and_escape_jsonstr(m, f->ntv_string, escape_slash); 99 | break; 100 | 101 | case NTV_BINARY: 102 | mbuf_append_and_escape_jsonstr(m, "binary", 0); 103 | break; 104 | 105 | case NTV_INT: 106 | snprintf(buf, sizeof(buf), "%" PRId64, f->ntv_s64); 107 | mbuf_append(m, buf, strlen(buf)); 108 | break; 109 | 110 | case NTV_DOUBLE: 111 | my_double2str(buf, sizeof(buf), f->ntv_double, precision, 112 | DBL_TYPE_FLOAT); 113 | mbuf_append(m, buf, strlen(buf)); 114 | break; 115 | 116 | case NTV_NULL: 117 | mbuf_append(m, "null", 4); 118 | break; 119 | 120 | case NTV_BOOLEAN: 121 | if(f->ntv_boolean) 122 | mbuf_append(m, "true", 4); 123 | else 124 | mbuf_append(m, "false", 5); 125 | break; 126 | 127 | } 128 | } 129 | 130 | /** 131 | * 132 | */ 133 | void 134 | ntv_json_serialize_ex(const ntv_t *msg, mbuf_t *m, int flags, int precision) 135 | { 136 | ntv_json_write_value(msg, m, 0, flags, precision); 137 | if(flags & (NTV_JSON_F_PRETTY | NTV_JSON_F_TRAILING_LF)) 138 | mbuf_append(m, "\n", 1); 139 | } 140 | 141 | /** 142 | * 143 | */ 144 | void 145 | ntv_json_serialize(const ntv_t *msg, mbuf_t *m, int flags) 146 | { 147 | ntv_json_serialize_ex(msg, m, flags, -1); 148 | } 149 | 150 | 151 | /** 152 | * 153 | */ 154 | char * 155 | ntv_json_serialize_to_str_ex(const ntv_t *msg, int flags, int precision) 156 | { 157 | if(msg == NULL) 158 | return NULL; 159 | 160 | mbuf_t m; 161 | mbuf_init(&m); 162 | ntv_json_serialize_ex(msg, &m, flags, precision); 163 | return mbuf_clear_to_string(&m); 164 | } 165 | 166 | 167 | /** 168 | * 169 | */ 170 | char * 171 | ntv_json_serialize_to_str(const ntv_t *msg, int flags) 172 | { 173 | return ntv_json_serialize_to_str_ex(msg, flags, -1); 174 | } 175 | 176 | /** 177 | * 178 | */ 179 | 180 | static void * 181 | create_map(void *opaque) 182 | { 183 | return ntv_create_map(); 184 | } 185 | 186 | static void * 187 | create_list(void *opaque) 188 | { 189 | return ntv_create_list(); 190 | } 191 | 192 | static void 193 | destroy_obj(void *opaque, void *obj) 194 | { 195 | return ntv_release(obj); 196 | } 197 | 198 | static void 199 | add_obj(void *opaque, void *parent, const char *name, void *child) 200 | { 201 | ntv_set_ntv(parent, name, child); 202 | } 203 | 204 | static void 205 | add_string(void *opaque, void *parent, const char *name, char *str) 206 | { 207 | ntv_set_str(parent, name, str); 208 | free(str); 209 | } 210 | 211 | static void 212 | add_long(void *opaque, void *parent, const char *name, long v) 213 | { 214 | ntv_set_int64(parent, name, v); 215 | } 216 | 217 | static void 218 | add_double(void *opaque, void *parent, const char *name, double v) 219 | { 220 | ntv_set_double(parent, name, v); 221 | } 222 | 223 | static void 224 | add_bool(void *opaque, void *parent, const char *name, int v) 225 | { 226 | ntv_set_boolean(parent, name, v); 227 | } 228 | 229 | static void 230 | add_null(void *opaque, void *parent, const char *name) 231 | { 232 | ntv_set_null(parent, name); 233 | } 234 | 235 | static void 236 | add_comment(void *opaque, void *parent, const char *comment) 237 | { 238 | // ntv_add_comment(parent, comment); 239 | } 240 | 241 | /** 242 | * 243 | */ 244 | static const json_deserializer_t json_to_ntv = { 245 | .jd_create_map = create_map, 246 | .jd_create_list = create_list, 247 | .jd_destroy_obj = destroy_obj, 248 | .jd_add_obj = add_obj, 249 | .jd_add_string = add_string, 250 | .jd_add_long = add_long, 251 | .jd_add_double = add_double, 252 | .jd_add_bool = add_bool, 253 | .jd_add_null = add_null, 254 | .jd_add_comment = add_comment, 255 | }; 256 | 257 | 258 | /** 259 | * 260 | */ 261 | ntv_t * 262 | ntv_json_deserialize(const char *src, char *errbuf, size_t errlen) 263 | { 264 | return json_deserialize(src, &json_to_ntv, NULL, errbuf, errlen); 265 | } 266 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | /* 9 | * Complete missing LIST-ops 10 | */ 11 | 12 | #ifndef LIST_FOREACH 13 | #define LIST_FOREACH(var, head, field) \ 14 | for ((var) = ((head)->lh_first); \ 15 | (var); \ 16 | (var) = ((var)->field.le_next)) 17 | #endif 18 | 19 | #ifndef LIST_EMPTY 20 | #define LIST_EMPTY(head) ((head)->lh_first == NULL) 21 | #endif 22 | 23 | #ifndef LIST_FIRST 24 | #define LIST_FIRST(head) ((head)->lh_first) 25 | #endif 26 | 27 | #ifndef LIST_NEXT 28 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 29 | #endif 30 | 31 | #ifndef LIST_INSERT_BEFORE 32 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 33 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 34 | (elm)->field.le_next = (listelm); \ 35 | *(listelm)->field.le_prev = (elm); \ 36 | (listelm)->field.le_prev = &(elm)->field.le_next; \ 37 | } while (/*CONSTCOND*/0) 38 | #endif 39 | 40 | /* 41 | * Complete missing TAILQ-ops 42 | */ 43 | 44 | #ifndef TAILQ_INSERT_BEFORE 45 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 46 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 47 | (elm)->field.tqe_next = (listelm); \ 48 | *(listelm)->field.tqe_prev = (elm); \ 49 | (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ 50 | } while (0) 51 | #endif 52 | 53 | #ifndef TAILQ_FOREACH 54 | #define TAILQ_FOREACH(var, head, field) \ 55 | for ((var) = ((head)->tqh_first); (var); (var) = ((var)->field.tqe_next)) 56 | #endif 57 | 58 | #ifndef TAILQ_FIRST 59 | #define TAILQ_FIRST(head) ((head)->tqh_first) 60 | #endif 61 | 62 | #ifndef TAILQ_NEXT 63 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 64 | #endif 65 | 66 | #ifndef TAILQ_LAST 67 | #define TAILQ_LAST(head, headname) \ 68 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 69 | #endif 70 | 71 | #ifndef TAILQ_PREV 72 | #define TAILQ_PREV(elm, headname, field) \ 73 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 74 | #endif 75 | 76 | /* 77 | * In Mac OS 10.4 and earlier TAILQ_FOREACH_REVERSE was defined 78 | * differently, redefined it. 79 | */ 80 | #ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ 81 | #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1050 82 | #undef TAILQ_FOREACH_REVERSE 83 | #endif 84 | #endif 85 | 86 | #ifndef TAILQ_FOREACH_REVERSE 87 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 88 | for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ 89 | (var); \ 90 | (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) 91 | #endif 92 | 93 | /* 94 | * Some extra functions for LIST manipulation 95 | */ 96 | 97 | #define LIST_INSERT_SORTED(head, elm, field, cmpfunc) do { \ 98 | if(LIST_EMPTY(head)) { \ 99 | LIST_INSERT_HEAD(head, elm, field); \ 100 | } else { \ 101 | typeof(elm) _tmp; \ 102 | LIST_FOREACH(_tmp,head,field) { \ 103 | if(cmpfunc(elm,_tmp) <= 0) { \ 104 | LIST_INSERT_BEFORE(_tmp,elm,field); \ 105 | break; \ 106 | } \ 107 | if(!LIST_NEXT(_tmp,field)) { \ 108 | LIST_INSERT_AFTER(_tmp,elm,field); \ 109 | break; \ 110 | } \ 111 | } \ 112 | } \ 113 | } while(0) 114 | 115 | #define TAILQ_INSERT_SORTED(head, elm, field, cmpfunc) do { \ 116 | if(TAILQ_FIRST(head) == NULL) { \ 117 | TAILQ_INSERT_HEAD(head, elm, field); \ 118 | } else { \ 119 | typeof(elm) _tmp; \ 120 | TAILQ_FOREACH(_tmp,head,field) { \ 121 | if(cmpfunc(elm,_tmp) <= 0) { \ 122 | TAILQ_INSERT_BEFORE(_tmp,elm,field); \ 123 | break; \ 124 | } \ 125 | if(!TAILQ_NEXT(_tmp,field)) { \ 126 | TAILQ_INSERT_AFTER(head,_tmp,elm,field); \ 127 | break; \ 128 | } \ 129 | } \ 130 | } \ 131 | } while(0) 132 | 133 | #define TAILQ_MOVE(newhead, oldhead, field) do { \ 134 | if(TAILQ_FIRST(oldhead)) { \ 135 | TAILQ_FIRST(oldhead)->field.tqe_prev = &(newhead)->tqh_first; \ 136 | (newhead)->tqh_last = (oldhead)->tqh_last; \ 137 | (newhead)->tqh_first = (oldhead)->tqh_first; \ 138 | TAILQ_INIT(oldhead); \ 139 | } else { \ 140 | TAILQ_INIT(newhead); \ 141 | } \ 142 | } while (/*CONSTCOND*/0) 143 | 144 | #define TAILQ_MERGE(q1, q2, field) do { \ 145 | if((q2)->tqh_first) { \ 146 | *(q1)->tqh_last = (q2)->tqh_first; \ 147 | (q2)->tqh_first->field.tqe_prev = (q1)->tqh_last; \ 148 | (q1)->tqh_last = (q2)->tqh_last; \ 149 | TAILQ_INIT(q2); \ 150 | } \ 151 | } while(/*CONSTCOND*/0) 152 | 153 | 154 | #ifndef SIMPLEQ_HEAD 155 | #define SIMPLEQ_HEAD(name, type) \ 156 | struct name { \ 157 | struct type *sqh_first; \ 158 | struct type **sqh_last; \ 159 | } 160 | #endif 161 | 162 | #ifndef SIMPLEQ_ENTRY 163 | #define SIMPLEQ_ENTRY(type) \ 164 | struct { \ 165 | struct type *sqe_next; \ 166 | } 167 | #endif 168 | 169 | #ifndef SIMPLEQ_FIRST 170 | #define SIMPLEQ_FIRST(head) ((head)->sqh_first) 171 | #endif 172 | 173 | #ifndef SIMPLEQ_REMOVE_HEAD 174 | #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ 175 | if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ 176 | (head)->sqh_last = &(head)->sqh_first; \ 177 | } while (0) 178 | #endif 179 | 180 | #ifndef SIMPLEQ_INSERT_TAIL 181 | #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 182 | (elm)->field.sqe_next = NULL; \ 183 | *(head)->sqh_last = (elm); \ 184 | (head)->sqh_last = &(elm)->field.sqe_next; \ 185 | } while (0) 186 | #endif 187 | 188 | #ifndef SIMPLEQ_INIT 189 | #define SIMPLEQ_INIT(head) do { \ 190 | (head)->sqh_first = NULL; \ 191 | (head)->sqh_last = &(head)->sqh_first; \ 192 | } while (0) 193 | #endif 194 | 195 | #ifndef SIMPLEQ_INSERT_HEAD 196 | #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 197 | if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ 198 | (head)->sqh_last = &(elm)->field.sqe_next; \ 199 | (head)->sqh_first = (elm); \ 200 | } while (0) 201 | #endif 202 | 203 | #ifndef SIMPLEQ_FOREACH 204 | #define SIMPLEQ_FOREACH(var, head, field) \ 205 | for((var) = SIMPLEQ_FIRST(head); \ 206 | (var) != SIMPLEQ_END(head); \ 207 | (var) = SIMPLEQ_NEXT(var, field)) 208 | #endif 209 | 210 | #ifndef SIMPLEQ_INSERT_AFTER 211 | #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 212 | if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \ 213 | (head)->sqh_last = &(elm)->field.sqe_next; \ 214 | (listelm)->field.sqe_next = (elm); \ 215 | } while (0) 216 | #endif 217 | 218 | #ifndef SIMPLEQ_END 219 | #define SIMPLEQ_END(head) NULL 220 | #endif 221 | 222 | #ifndef SIMPLEQ_NEXT 223 | #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) 224 | #endif 225 | 226 | #ifndef SIMPLEQ_HEAD_INITIALIZER 227 | #define SIMPLEQ_HEAD_INITIALIZER(head) \ 228 | { NULL, &(head).sqh_first } 229 | #endif 230 | 231 | #ifndef SIMPLEQ_EMPTY 232 | #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) 233 | #endif 234 | 235 | -------------------------------------------------------------------------------- /skeleton/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2014 Andreas Öman 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | # 17 | 18 | 19 | PROGNAME := example 20 | 21 | WITH_HTTP_SERVER := yes 22 | WITH_CTRL_SOCK := yes 23 | WITH_CURL := yes 24 | WITH_WS_CLIENT := yes 25 | WITH_ASYNCIO := yes 26 | 27 | CC=gcc 28 | 29 | BUILD ?= $(shell uname) 30 | 31 | -include mk/${BUILD}.mk 32 | 33 | CSTANDARD := gnu11 34 | CFLAGS_opt := -O2 35 | 36 | BUILDDIR = ${CURDIR}/build.${BUILD} 37 | 38 | PROG=${BUILDDIR}/${PROGNAME} 39 | 40 | SRCS = src/main.c \ 41 | 42 | LDFLAGS += -lz 43 | 44 | install: ${PROG} 45 | install -D ${PROG} "${prefix}/bin/${PROGNAME}" 46 | uninstall: 47 | rm -f "${prefix}/bin/${PROGNAME}" "${prefix}/bin/${PROGNAME}" 48 | 49 | include libsvc/libsvc.mk 50 | -include config.local 51 | -include $(DEPS) 52 | 53 | -------------------------------------------------------------------------------- /skeleton/mk/Darwin.mk: -------------------------------------------------------------------------------- 1 | 2 | HOMEBREW_LOC=$(shell dirname $(shell which brew)) 3 | HOMEBREW_PREFIX=$(shell (cd ${HOMEBREW_LOC}/.. && pwd)) 4 | 5 | PKG_CONFIG_PATH := ${HOMEBREW_PREFIX}/opt/openssl/lib/pkgconfig/ 6 | 7 | PKG_CONFIG := PKG_CONFIG_PATH='$(PKG_CONFIG_PATH)' pkg-config 8 | 9 | CFLAGS += ${CFLAGS_deps} 10 | -------------------------------------------------------------------------------- /skeleton/mk/Linux.mk: -------------------------------------------------------------------------------- 1 | CFLAGS_deps := $(shell PKG_CONFIG_PATH='$(PKG_CONFIG_PATH)' PKG_CONFIG_SYSROOT_DIR='$(PKG_CONFIG_SYSROOT_DIR)' pkg-config openssl --cflags) 2 | 3 | LDFLAGS += $(shell PKG_CONFIG_PATH='$(PKG_CONFIG_PATH)' PKG_CONFIG_SYSROOT_DIR='$(PKG_CONFIG_SYSROOT_DIR)' pkg-config openssl --libs) 4 | 5 | CFLAGS += ${CFLAGS_deps} 6 | -------------------------------------------------------------------------------- /skeleton/mk/asan.mk: -------------------------------------------------------------------------------- 1 | -include mk/$(shell uname).mk 2 | 3 | CFLAGS_opt := -fno-omit-frame-pointer 4 | CFLAGS_opt += -fsanitize=address 5 | 6 | ifeq ($(shell uname),Linux) 7 | CFLAGS_opt += -Og 8 | else 9 | CFLAGS_opt += -O0 10 | endif 11 | 12 | LDFLAGS += -fsanitize=address 13 | -------------------------------------------------------------------------------- /skeleton/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "libsvc/libsvc.h" 14 | #include "libsvc/cfg.h" 15 | #include "libsvc/trace.h" 16 | 17 | 18 | /** 19 | * 20 | */ 21 | static void 22 | handle_sigpipe(int x) 23 | { 24 | return; 25 | } 26 | 27 | 28 | /** 29 | * 30 | */ 31 | int 32 | main(int argc, char **argv) 33 | { 34 | char errbuf[512]; 35 | int c; 36 | sigset_t set; 37 | const char *cfgfile = PROGNAME".json"; 38 | 39 | signal(SIGPIPE, handle_sigpipe); 40 | 41 | while((c = getopt(argc, argv, "c:s:")) != -1) { 42 | switch(c) { 43 | case 'c': 44 | cfgfile = optarg; 45 | break; 46 | case 's': 47 | enable_syslog(PROGNAME, optarg); 48 | break; 49 | } 50 | } 51 | 52 | sigfillset(&set); 53 | sigdelset(&set, SIGQUIT); 54 | sigdelset(&set, SIGILL); 55 | sigdelset(&set, SIGTRAP); 56 | sigdelset(&set, SIGABRT); 57 | sigdelset(&set, SIGFPE); 58 | sigdelset(&set, SIGBUS); 59 | sigdelset(&set, SIGSEGV); 60 | sigdelset(&set, SIGSYS); 61 | sigprocmask(SIG_BLOCK, &set, NULL); 62 | 63 | srand48(getpid() ^ time(NULL)); 64 | 65 | if(cfg_load(cfgfile, errbuf, sizeof(errbuf))) { 66 | fprintf(stderr, "Unable to load config -- %s " 67 | "(check -c option). Giving up\n", errbuf); 68 | exit(1); 69 | } 70 | 71 | libsvc_init(); 72 | 73 | 74 | trace(LOG_WARNING, "Running pid %d", getpid()); 75 | while(1) { 76 | int delivered = 0; 77 | if(!sigwait(&set, &delivered)) { 78 | trace(LOG_DEBUG, "Main loop got signal %d", delivered); 79 | if(delivered == SIGTERM || delivered == SIGINT) 80 | break; 81 | 82 | if(delivered == SIGCHLD) { 83 | while(waitpid(-1, NULL, WNOHANG) > 0); 84 | } 85 | 86 | if(delivered == SIGHUP) { 87 | cfg_load(NULL, NULL, 0); 88 | } 89 | } 90 | } 91 | 92 | trace(LOG_WARNING, "Stopping"); 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /sock.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #ifdef linux 25 | #define _GNU_SOURCE 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "sock.h" 34 | 35 | /** 36 | * 37 | */ 38 | int 39 | libsvc_accept(int fd, struct sockaddr *sa, socklen_t *addrlen) 40 | { 41 | int r; 42 | #ifdef linux 43 | r = accept4(fd, sa, addrlen, SOCK_CLOEXEC); 44 | #else 45 | r = accept(fd, sa, addrlen); 46 | if(r >= 0) 47 | fcntl(r, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 48 | #endif 49 | return r; 50 | } 51 | 52 | 53 | /** 54 | * 55 | */ 56 | int 57 | libsvc_socket(int domain, int type, int protocol) 58 | { 59 | int fd; 60 | #ifdef linux 61 | fd = socket(domain, type | SOCK_CLOEXEC, protocol); 62 | #else 63 | fd = socket(domain, type, protocol); 64 | if(fd >= 0) 65 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 66 | #endif 67 | return fd; 68 | } 69 | 70 | /** 71 | * 72 | */ 73 | int 74 | libsvc_pipe(int pipefd[2]) 75 | { 76 | #ifdef linux 77 | return pipe2(pipefd, O_CLOEXEC); 78 | #else 79 | if(pipe(pipefd) == -1) 80 | return -1; 81 | fcntl(pipefd[0], F_SETFD, fcntl(pipefd[0], F_GETFD) | FD_CLOEXEC); 82 | fcntl(pipefd[1], F_SETFD, fcntl(pipefd[1], F_GETFD) | FD_CLOEXEC); 83 | return 0; 84 | #endif 85 | } 86 | 87 | -------------------------------------------------------------------------------- /sock.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | struct sockaddr; 29 | int libsvc_accept(int fd, struct sockaddr *sa, socklen_t *addrlen); 30 | 31 | int libsvc_socket(int domain, int type, int protocol); 32 | 33 | int libsvc_pipe(int pipefd[2]); 34 | -------------------------------------------------------------------------------- /sources.mk: -------------------------------------------------------------------------------- 1 | 2 | PKG_CONFIG ?= pkg-config 3 | 4 | libsvc_SRCS += \ 5 | libsvc.c \ 6 | misc.c \ 7 | task.c \ 8 | htsbuf.c \ 9 | json.c \ 10 | dbl.c \ 11 | dial.c \ 12 | dns.c \ 13 | utf8.c \ 14 | tcp.c \ 15 | trace.c \ 16 | irc.c \ 17 | cfg.c \ 18 | cmd.c \ 19 | talloc.c \ 20 | memstream.c \ 21 | sock.c \ 22 | ntv.c \ 23 | ntv_json.c \ 24 | ntv_binary.c \ 25 | ntv_msgpack.c \ 26 | ntv_cbor.c \ 27 | ntv_xml.c \ 28 | intvec.c \ 29 | strvec.c \ 30 | murmur3.c \ 31 | mbuf.c \ 32 | trap.c \ 33 | err.c \ 34 | aws.c \ 35 | acme.c \ 36 | fpipe.c \ 37 | tbm.c \ 38 | cookie.c \ 39 | gcp.c \ 40 | azure.c \ 41 | 42 | 43 | libsvc_INCS += \ 44 | libsvc.h \ 45 | misc.h \ 46 | task.h \ 47 | htsbuf.h \ 48 | json.h \ 49 | dbl.h \ 50 | dial.h \ 51 | utf8.h \ 52 | tcp.h \ 53 | trace.h \ 54 | irc.h \ 55 | cfg.h \ 56 | cmd.h \ 57 | talloc.h \ 58 | memstream.h \ 59 | sock.h \ 60 | intvec.h \ 61 | strvec.h \ 62 | init.h \ 63 | murmur3.h \ 64 | mbuf.h \ 65 | 66 | ifeq (${WITH_OPENSSL},yes) 67 | CFLAGS += $(shell $(PKG_CONFIG) --cflags openssl) -DWITH_OPENSSL 68 | LDFLAGS += $(shell $(PKG_CONFIG) --libs openssl) 69 | endif 70 | 71 | 72 | 73 | ifeq ($(shell uname),Linux) 74 | LDFLAGS += -ldl #for trap handler 75 | endif 76 | 77 | libsvc_SRCS += http_client.c 78 | libsvc_INCS += http_client.h 79 | 80 | ############################################################## 81 | # Curl 82 | ############################################################## 83 | ifeq (${WITH_CURL},yes) 84 | 85 | CFLAGS += -DWITH_CURL 86 | 87 | ifeq ($(shell uname),Darwin) 88 | LDFLAGS += -lcurl -lz -liconv 89 | endif 90 | 91 | ifeq ($(shell uname),Linux) 92 | CFLAGS += $(shell $(PKG_CONFIG) --cflags libcurl) 93 | LDFLAGS += $(shell $(PKG_CONFIG) --libs libcurl) 94 | endif 95 | 96 | libsvc_SRCS += http_client_curl.c 97 | libsvc_SRCS += curlhelpers.c 98 | 99 | libsvc_INCS += curlhelpers.h 100 | 101 | else 102 | 103 | libsvc_SRCS += http_client_builtin.c 104 | WITH_HTTP_PARSER := yes 105 | 106 | endif 107 | 108 | ############################################################## 109 | # MYSQL 110 | ############################################################## 111 | 112 | ifeq (${WITH_MYSQL},yes) 113 | libsvc_SRCS += db.c 114 | libsvc_INCS += db.h 115 | CFLAGS += $(shell mysql_config --cflags) -DWITH_MYSQL 116 | LDFLAGS += $(shell mysql_config --libs_r) 117 | endif 118 | 119 | ############################################################## 120 | # Websocket client 121 | ############################################################## 122 | 123 | ifeq (${WITH_WS_CLIENT},yes) 124 | WITH_HTTP_PARSER := yes 125 | libsvc_SRCS += websocket_client.c 126 | libsvc_INCS += websocket_client.h 127 | WITH_WEBSOCKET := yes 128 | CFLAGS += -DWITH_WS_CLIENT 129 | endif 130 | 131 | ############################################################## 132 | # HTTP Server 133 | ############################################################## 134 | 135 | ifeq (${WITH_HTTP_SERVER},yes) 136 | libsvc_SRCS += http.c 137 | libsvc_INCS += http.h 138 | WITH_ASYNCIO := yes 139 | WITH_WEBSOCKET := yes 140 | WITH_HTTP_PARSER := yes 141 | CFLAGS += -DWITH_HTTP_SERVER 142 | LDFLAGS += -lz 143 | endif 144 | 145 | ifeq (${WITH_HTTP_PARSER},yes) 146 | libsvc_SRCS += http_parser.c 147 | endif 148 | 149 | ############################################################## 150 | # TCP server 151 | ############################################################## 152 | 153 | ifeq (${WITH_TCP_SERVER},yes) 154 | CFLAGS += -DWITH_TCP_SERVER 155 | libsvc_SRCS += tcp_server.c 156 | endif 157 | 158 | ############################################################## 159 | # AsyncIO 160 | ############################################################## 161 | 162 | ifeq (${WITH_ASYNCIO},yes) 163 | libsvc_SRCS += asyncio.c stream.c 164 | libsvc_INCS += asyncio.h stream.h 165 | CFLAGS += -DWITH_ASYNCIO 166 | endif 167 | 168 | ############################################################## 169 | # Control socket 170 | ############################################################## 171 | ifeq (${WITH_CTRLSOCK},yes) 172 | libsvc_SRCS += ctrlsock.c 173 | libsvc_INCS += ctrlsock.h 174 | CFLAGS += -DWITH_CTRLSOCK 175 | endif 176 | 177 | ############################################################## 178 | # Websocket common 179 | ############################################################## 180 | 181 | ifeq (${WITH_WEBSOCKET},yes) 182 | libsvc_SRCS += websocket.c 183 | libsvc_INCS += websocket.h 184 | LDFLAGS += -lz 185 | endif 186 | -------------------------------------------------------------------------------- /stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "stream.h" 9 | #include "dial.h" 10 | #include "asyncio.h" 11 | #include "atomic.h" 12 | #include "trace.h" 13 | #include "misc.h" 14 | 15 | struct stream { 16 | asyncio_fd_t *s_af; 17 | atomic_t s_refcount; 18 | int s_eos; 19 | int s_error; 20 | mbuf_t s_recv_buf; 21 | pthread_mutex_t s_recv_mutex; 22 | pthread_cond_t s_recv_cond; 23 | }; 24 | 25 | 26 | static void 27 | stream_release(stream_t *s) 28 | { 29 | if(atomic_dec(&s->s_refcount)) 30 | return; 31 | 32 | asyncio_fd_release(s->s_af); 33 | mbuf_clear(&s->s_recv_buf); 34 | pthread_mutex_destroy(&s->s_recv_mutex); 35 | pthread_cond_destroy(&s->s_recv_cond); 36 | free(s); 37 | } 38 | 39 | 40 | static void 41 | stream_error(void *opaque, int error) 42 | { 43 | stream_t *s = opaque; 44 | pthread_mutex_lock(&s->s_recv_mutex); 45 | s->s_eos = 1; 46 | s->s_error = error; 47 | pthread_cond_signal(&s->s_recv_cond); 48 | pthread_mutex_unlock(&s->s_recv_mutex); 49 | asyncio_close(s->s_af); 50 | stream_release(s); 51 | } 52 | 53 | 54 | static void 55 | stream_bytes_avail(void *opaque, struct mbuf *mq) 56 | { 57 | stream_t *s = opaque; 58 | pthread_mutex_lock(&s->s_recv_mutex); 59 | mbuf_appendq(&s->s_recv_buf, mq); 60 | pthread_cond_signal(&s->s_recv_cond); 61 | pthread_mutex_unlock(&s->s_recv_mutex); 62 | } 63 | 64 | 65 | stream_t * 66 | stream_connect(const char *hostname, int port, int timeout_ms, 67 | char *errbuf, size_t errlen, int flags) 68 | { 69 | pthread_condattr_t cond_attr; 70 | pthread_condattr_init(&cond_attr); 71 | if(flags & STREAM_CLOCK_MONOTONIC) { 72 | #ifdef __linux__ 73 | pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); 74 | #else 75 | trace(LOG_ERR, "stream: STREAM_CLOCK_MONOTIME not supported on this machine"); 76 | return NULL; 77 | #endif 78 | } 79 | 80 | if(flags & STREAM_DEBUG) 81 | trace(LOG_DEBUG, "stream: Connecting to %s:%d", hostname, port); 82 | int fd = dialfd(hostname, port, timeout_ms, errbuf, errlen, 83 | flags & STREAM_DEBUG); 84 | if(flags & STREAM_DEBUG) 85 | trace(LOG_DEBUG, "stream: Connect %s:%d : %s", 86 | hostname, port, fd == -1 ? strerror(errno) : "OK"); 87 | if(fd == -1) 88 | return NULL; 89 | 90 | stream_t *s = calloc(1, sizeof(stream_t)); 91 | atomic_set(&s->s_refcount, 2); 92 | 93 | mbuf_init(&s->s_recv_buf); 94 | pthread_cond_init(&s->s_recv_cond, &cond_attr); 95 | pthread_condattr_destroy(&cond_attr); 96 | pthread_mutex_init(&s->s_recv_mutex, NULL); 97 | 98 | asyncio_sslctx_t *sslctx = NULL; 99 | 100 | if(flags & STREAM_CONNECT_F_SSL) { 101 | if(flags & STREAM_DEBUG) 102 | trace(LOG_DEBUG, "stream: Initializing TLS context for %s:%d", hostname, port); 103 | sslctx = asyncio_sslctx_client(); 104 | if(flags & STREAM_DEBUG) 105 | trace(LOG_DEBUG, "stream: Initialized TLS context for %s:%d", hostname, port); 106 | } 107 | 108 | int asyncio_flags = ASYNCIO_FLAG_THREAD_SAFE; 109 | if(!(flags & STREAM_CONNECT_F_SSL_DONT_VERIFY)) 110 | asyncio_flags |= ASYNCIO_FLAG_SSL_VERIFY_CERT; 111 | 112 | s->s_af = asyncio_stream(fd, stream_bytes_avail, stream_error, 113 | s, asyncio_flags, sslctx, hostname, hostname, 114 | NULL); 115 | if(sslctx != NULL) 116 | asyncio_sslctx_free(sslctx); 117 | 118 | if(flags & STREAM_DEBUG) 119 | trace(LOG_DEBUG, "stream: Stream for %s:%d initialized", hostname, port); 120 | 121 | return s; 122 | } 123 | 124 | 125 | ssize_t 126 | stream_write(stream_t *s, const void *data, size_t len) 127 | { 128 | if(asyncio_send(s->s_af, data, len, 0) == -1) { 129 | errno = ECONNRESET; 130 | return -1; 131 | } 132 | return len; 133 | } 134 | 135 | 136 | ssize_t 137 | stream_read_timeout(stream_t *s, void *data, size_t len, int flags, 138 | int64_t deadline) 139 | { 140 | struct timespec ts, *tsp = NULL; 141 | 142 | if(deadline) { 143 | ts.tv_sec = deadline / 1000000; 144 | ts.tv_nsec = (deadline % 1000000) * 1000; 145 | tsp = &ts; 146 | } 147 | 148 | pthread_mutex_lock(&s->s_recv_mutex); 149 | while(1) { 150 | if(s->s_recv_buf.mq_size == 0 && s->s_eos) { 151 | if(flags == 0 || s->s_error == 0) { 152 | pthread_mutex_unlock(&s->s_recv_mutex); 153 | return 0; 154 | } 155 | 156 | errno = s->s_error; 157 | pthread_mutex_unlock(&s->s_recv_mutex); 158 | return -1; 159 | } 160 | 161 | int do_wait = 0; 162 | if(flags & STREAM_READ_F_ALL) { 163 | if(s->s_recv_buf.mq_size < len) { 164 | do_wait = 1; 165 | } 166 | } else { 167 | if(s->s_recv_buf.mq_size == 0) { 168 | do_wait = 1; 169 | } 170 | } 171 | 172 | if(do_wait) { 173 | if(tsp) { 174 | int r = pthread_cond_timedwait(&s->s_recv_cond, &s->s_recv_mutex, tsp); 175 | if(r) { 176 | errno = r; 177 | pthread_mutex_unlock(&s->s_recv_mutex); 178 | return -1; 179 | } 180 | } else { 181 | pthread_cond_wait(&s->s_recv_cond, &s->s_recv_mutex); 182 | } 183 | continue; 184 | } 185 | 186 | int r = MIN(s->s_recv_buf.mq_size, len); 187 | mbuf_read(&s->s_recv_buf, data, r); 188 | pthread_mutex_unlock(&s->s_recv_mutex); 189 | return r; 190 | } 191 | } 192 | 193 | ssize_t 194 | stream_read(stream_t *s, void *data, size_t len, int flags) 195 | { 196 | return stream_read_timeout(s, data, len, flags, 0); 197 | } 198 | 199 | void 200 | stream_close(stream_t *s) 201 | { 202 | asyncio_shutdown(s->s_af); 203 | stream_release(s); 204 | } 205 | 206 | 207 | void 208 | stream_shutdown(stream_t *s, int stop_reader) 209 | { 210 | if(stop_reader) { 211 | pthread_mutex_lock(&s->s_recv_mutex); 212 | s->s_eos = 1; 213 | s->s_error = 0; 214 | pthread_cond_signal(&s->s_recv_cond); 215 | pthread_mutex_unlock(&s->s_recv_mutex); 216 | } 217 | asyncio_shutdown(s->s_af); 218 | } 219 | -------------------------------------------------------------------------------- /stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct stream stream_t; 6 | 7 | #define STREAM_CONNECT_F_SSL 0x1 8 | #define STREAM_CONNECT_F_SSL_DONT_VERIFY 0x2 9 | #define STREAM_DEBUG 0x4 10 | #define STREAM_CLOCK_MONOTONIC 0x8 11 | 12 | stream_t *stream_connect(const char *hostname, int port, 13 | int timeout_ms, 14 | char *errbuf, size_t errlen, 15 | int flags); 16 | 17 | // Return number of bytes written or -1 on error (which sets errno) 18 | ssize_t stream_write(stream_t *s, const void *data, size_t len); 19 | 20 | #define STREAM_READ_F_ALL 0x1 21 | // Return number of bytes read or -1 on error (which sets errno) 22 | ssize_t stream_read(stream_t *s, void *data, size_t len, int flags); 23 | 24 | // Same as above but with timeout (in µs since 1970) 25 | ssize_t stream_read_timeout(stream_t *s, void *data, size_t len, int flags, 26 | int64_t deadline); 27 | 28 | void stream_close(stream_t *s); 29 | 30 | void stream_shutdown(stream_t *s, int stop_reader); 31 | -------------------------------------------------------------------------------- /strtab.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andoma/libsvc/30e9b790016bf64c753f53a151e48cc2853a8395/strtab.h -------------------------------------------------------------------------------- /strvec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "strvec.h" 7 | #include "misc.h" 8 | 9 | static void 10 | strvec_inc(strvec_t *vec) 11 | { 12 | if(vec->count + 1 >= vec->capacity) { 13 | vec->capacity = vec->capacity * 2 + 16; 14 | vec->v = realloc(vec->v, sizeof(vec->v[0]) * vec->capacity); 15 | } 16 | } 17 | 18 | 19 | void 20 | strvec_push(strvec_t *vec, const char *value) 21 | { 22 | strvec_inc(vec); 23 | vec->v[vec->count++] = value ? strdup(value) : NULL; 24 | } 25 | 26 | void 27 | strvec_push_alloced(strvec_t *vec, char *value) 28 | { 29 | strvec_inc(vec); 30 | vec->v[vec->count++] = value; 31 | } 32 | 33 | 34 | void 35 | strvec_pushl(strvec_t *vec, const char *value, size_t len) 36 | { 37 | char *x = malloc_add(len, 1); 38 | memcpy(x, value, len); 39 | x[len] = 0; 40 | strvec_push_alloced(vec, x); 41 | } 42 | 43 | 44 | void 45 | strvec_pushf(strvec_t *vec, const char *fmt, ...) 46 | { 47 | va_list ap; 48 | va_start(ap, fmt); 49 | strvec_push_alloced(vec, fmtv(fmt, ap)); 50 | va_end(ap); 51 | } 52 | 53 | void 54 | strvec_reset(strvec_t *vec) 55 | { 56 | for(int i = 0; i < vec->count; i++) 57 | free(vec->v[i]); 58 | vec->count = 0; 59 | vec->capacity = 0; 60 | free(vec->v); 61 | vec->v = NULL; 62 | } 63 | 64 | void 65 | strvec_insert(strvec_t *vec, unsigned int position, const char *value) 66 | { 67 | if(position == vec->count) 68 | return strvec_push(vec, value); 69 | 70 | if(vec->count + 1 >= vec->capacity) { 71 | vec->capacity = vec->capacity * 2 + 16; 72 | vec->v = realloc(vec->v, sizeof(vec->v[0]) * vec->capacity); 73 | } 74 | 75 | memmove(vec->v + position + 1, vec->v + position, 76 | (vec->count - position) * sizeof(vec->v[0])); 77 | 78 | vec->v[position] = strdup(value); 79 | vec->count++; 80 | } 81 | 82 | 83 | void 84 | strvec_delete(strvec_t *vec, unsigned int position) 85 | { 86 | assert(position < vec->count); 87 | free(vec->v[position]); 88 | memmove(vec->v + position, vec->v + position + 1, 89 | (vec->count - position - 1) * sizeof(vec->v[0])); 90 | vec->count--; 91 | } 92 | 93 | 94 | static int 95 | strvec_search(const strvec_t *vec, const char *value) 96 | { 97 | int imin = 0; 98 | int imax = vec->count; 99 | 100 | while(imin < imax) { 101 | int imid = (imin + imax) >> 1; 102 | 103 | if(strcmp(vec->v[imid], value) < 0) 104 | imin = imid + 1; 105 | else 106 | imax = imid; 107 | } 108 | return imin; 109 | } 110 | 111 | 112 | int 113 | strvec_insert_sorted(strvec_t *vec, const char *value) 114 | { 115 | int position = strvec_search(vec, value); 116 | strvec_insert(vec, position, value); 117 | return position; 118 | } 119 | 120 | 121 | int 122 | strvec_find(const strvec_t *vec, const char *value) 123 | { 124 | if(vec->count == 0) 125 | return -1; 126 | const int pos = strvec_search(vec, value); 127 | return pos < vec->count && !strcmp(vec->v[pos], value) ? pos : -1; 128 | } 129 | 130 | 131 | int 132 | strvec_delete_value(strvec_t *vec, const char *value) 133 | { 134 | if(vec->count == 0) 135 | return -1; 136 | const int pos = strvec_find(vec, value); 137 | if(pos >= 0) 138 | strvec_delete(vec, pos); 139 | return pos; 140 | } 141 | 142 | void 143 | strvec_copy(strvec_t *dst, const strvec_t *src) 144 | { 145 | // We trim the capacity down to the actual size here 146 | dst->count = dst->capacity = src->count; 147 | 148 | dst->v = malloc_mul(dst->count, sizeof(dst->v[0])); 149 | for(int i = 0; i < dst->count; i++) 150 | dst->v[i] = src->v[i] ? strdup(src->v[i]) : NULL; 151 | } 152 | 153 | 154 | char * 155 | strvec_join(const strvec_t *src, const char *sep) 156 | { 157 | int totlen = 1; 158 | const int seplen = strlen(sep); 159 | for(int i = 0; i < src->count; i++) { 160 | if(src->v[i]) 161 | totlen += strlen(src->v[i]) + (i ? seplen : 0); 162 | } 163 | 164 | char *r = malloc(totlen); 165 | int off = 0; 166 | for(int i = 0; i < src->count; i++) { 167 | if(src->v[i]) { 168 | if(i) { 169 | memcpy(r + off, sep, seplen); 170 | off += seplen; 171 | } 172 | const int len = strlen(src->v[i]); 173 | memcpy(r + off, src->v[i], len); 174 | off += len; 175 | } 176 | } 177 | r[off] = 0; 178 | return r; 179 | } 180 | 181 | 182 | void 183 | strvec_split(strvec_t *dst, const char *str, const char *sep, int include_empty) 184 | { 185 | size_t seplen = strlen(sep); 186 | while(str) { 187 | const char *next = strstr(str, sep); 188 | size_t len = next ? next - str : strlen(str); 189 | if(len > 0 || include_empty) 190 | strvec_pushl(dst, str, len); 191 | 192 | if(next) 193 | next += seplen; 194 | str = next; 195 | } 196 | } 197 | 198 | 199 | int 200 | strvec_eq(const strvec_t *a, const strvec_t *b) 201 | { 202 | if(a->count != b->count) 203 | return 0; 204 | for(int i = 0; i < a->count; i++) { 205 | if(strcmp(strvec_get(a, i), strvec_get(b, i))) 206 | return 0; 207 | } 208 | return 1; 209 | } 210 | -------------------------------------------------------------------------------- /strvec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct strvec { 4 | size_t capacity; 5 | size_t count; 6 | char **v; 7 | } strvec_t; 8 | 9 | 10 | void strvec_push(strvec_t *vec, const char *value); 11 | 12 | void strvec_pushl(strvec_t *vec, const char *value, size_t len); 13 | 14 | void strvec_pushf(strvec_t *vec, const char *fmt, ...) 15 | __attribute__ ((format (printf, 2, 3))); 16 | 17 | void strvec_push_alloced(strvec_t *vec, char *value); 18 | 19 | void strvec_reset(strvec_t *vec); 20 | 21 | void strvec_insert(strvec_t *vec, unsigned int position, const char *value); 22 | 23 | void strvec_delete(strvec_t *vec, unsigned int position); 24 | 25 | int strvec_delete_value(strvec_t *vec, const char *value); 26 | 27 | int strvec_insert_sorted(strvec_t *vec, const char *value); 28 | 29 | int strvec_find(const strvec_t *vec, const char *value); 30 | 31 | void strvec_copy(strvec_t *dst, const strvec_t *src); 32 | 33 | int strvec_eq(const strvec_t *a, const strvec_t *b); 34 | 35 | char *strvec_join(const strvec_t *src, const char *sep); 36 | 37 | void strvec_split(strvec_t *dst, const char *str, 38 | const char *sep, int include_empty); 39 | 40 | #define strvec_get(x, i) (x)->v[i] 41 | 42 | #define scoped_strvec(x) strvec_t x __attribute__((cleanup(strvec_reset))) = {} 43 | 44 | -------------------------------------------------------------------------------- /talloc.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "talloc.h" 31 | #include "misc.h" 32 | 33 | typedef struct talloc_item { 34 | struct talloc_item *next; 35 | } talloc_item_t; 36 | 37 | //static talloc_item_t __thread *talloc_queue; 38 | static pthread_key_t talloc_key; 39 | 40 | 41 | 42 | /** 43 | * 44 | */ 45 | static talloc_item_t ** 46 | talloc_getq(void) 47 | { 48 | talloc_item_t **q = pthread_getspecific(talloc_key); 49 | if(q) 50 | return q; 51 | 52 | q = calloc(1, sizeof(talloc_item_t *)); 53 | pthread_setspecific(talloc_key, q); 54 | return q; 55 | } 56 | 57 | 58 | 59 | /** 60 | * 61 | */ 62 | static void 63 | talloc_free_items(talloc_item_t **q) 64 | { 65 | talloc_item_t *p, *next; 66 | for(p = *q; p != NULL; p = next) { 67 | next = p->next; 68 | free(p); 69 | } 70 | *q = NULL; 71 | } 72 | 73 | 74 | /** 75 | * 76 | */ 77 | static void 78 | talloc_thread_cleanup(void *aux) 79 | { 80 | talloc_free_items(aux); 81 | free(aux); 82 | } 83 | 84 | 85 | /** 86 | * 87 | */ 88 | void 89 | talloc_cleanup(void) 90 | { 91 | talloc_item_t **q = pthread_getspecific(talloc_key); 92 | if(q != NULL) 93 | talloc_free_items(q); 94 | } 95 | 96 | 97 | /** 98 | * 99 | */ 100 | static void 101 | talloc_insert(talloc_item_t *t) 102 | { 103 | talloc_item_t **q = talloc_getq(); 104 | t->next = *q; 105 | *q = t; 106 | } 107 | 108 | 109 | /** 110 | * 111 | */ 112 | void * 113 | talloc_malloc(size_t s) 114 | { 115 | talloc_item_t *t = malloc_add(s, sizeof(talloc_item_t)); 116 | talloc_insert(t); 117 | return t + 1; 118 | } 119 | 120 | 121 | /** 122 | * 123 | */ 124 | void * 125 | talloc_zalloc(size_t s) 126 | { 127 | void *x = talloc_malloc(s); 128 | if(x != NULL) 129 | memset(x, 0, s); 130 | return x; 131 | } 132 | 133 | 134 | /** 135 | * 136 | */ 137 | char * 138 | tstrdup(const char *str) 139 | { 140 | if(str == NULL) 141 | return NULL; 142 | size_t len = strlen(str); 143 | char *r = talloc_malloc(len + 1); 144 | memcpy(r, str, len); 145 | r[len] = 0; 146 | return r; 147 | } 148 | 149 | 150 | /** 151 | * 152 | */ 153 | char * 154 | tsprintf(const char *fmt, ...) 155 | { 156 | char buf[100]; 157 | va_list ap; 158 | int n; 159 | 160 | va_start(ap, fmt); 161 | n = vsnprintf(buf, sizeof(buf), fmt, ap); 162 | va_end(ap); 163 | 164 | if(n < 0) 165 | abort(); 166 | 167 | if(n < sizeof(buf)) 168 | return tstrdup(buf); 169 | 170 | char *b = talloc_malloc(n + 1); 171 | 172 | va_start(ap, fmt); 173 | vsnprintf(b, n + 1, fmt, ap); 174 | va_end(ap); 175 | return b; 176 | } 177 | 178 | 179 | /** 180 | * 181 | */ 182 | static void __attribute__((constructor)) 183 | talloc_init(void) 184 | { 185 | pthread_key_create(&talloc_key, talloc_thread_cleanup); 186 | } 187 | 188 | 189 | -------------------------------------------------------------------------------- /talloc.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | void *talloc_malloc(size_t s); 29 | 30 | void *talloc_zalloc(size_t s); 31 | 32 | void talloc_cleanup(void); 33 | 34 | char *tstrdup(const char *str); 35 | 36 | char *tsprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 37 | -------------------------------------------------------------------------------- /task.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | typedef struct task_group task_group_t; 29 | 30 | typedef void (task_fn_t)(void *opaque); 31 | 32 | void task_run(task_fn_t *fn, void *opaque); 33 | 34 | task_group_t *task_group_create(void); 35 | 36 | task_group_t *task_group_create_with_concurrency(int max_concurrency); 37 | 38 | void task_group_destroy(task_group_t *tg); 39 | 40 | void task_run_in_group(task_fn_t *fn, void *opaque, task_group_t *tg); 41 | 42 | void task_stop(void); 43 | 44 | typedef struct task_stats { 45 | uint32_t num_threads; 46 | uint32_t idle_threads; 47 | uint64_t tasks_enqueued; 48 | } task_stats_t; 49 | 50 | void task_get_stats(task_stats_t *stats); 51 | 52 | int task_system_overload(void); 53 | -------------------------------------------------------------------------------- /tbm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tbm.h" 5 | 6 | 7 | /** 8 | * 9 | */ 10 | uint64_t 11 | tbm_withdraw(token_bucket_meter_t *tb, double amount, uint64_t now) 12 | { 13 | if(tb->tokens < amount) { 14 | const uint64_t delta = now - tb->last_fill; 15 | const double add = tb->rate * delta / 1000000.0; 16 | tb->tokens = MIN(tb->tokens + add, tb->burst); 17 | tb->last_fill = now; 18 | 19 | if(tb->tokens < amount) { 20 | return 1 + amount * 1e6 / tb->rate; 21 | } 22 | } 23 | tb->tokens -= amount; 24 | return 0; 25 | } 26 | 27 | 28 | void 29 | tbm_init(token_bucket_meter_t *tb, double rate, double burst) 30 | { 31 | tb->last_fill = 0; 32 | tb->tokens = 0; 33 | tb->burst = burst; 34 | tb->rate = rate; 35 | } 36 | -------------------------------------------------------------------------------- /tbm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | typedef struct token_bucket_meter { 7 | uint64_t last_fill; 8 | double tokens; 9 | double burst; 10 | double rate; 11 | } token_bucket_meter_t; 12 | 13 | 14 | // 'now' is current time in microseconds 15 | // Returns 0 if granted, otherwise returns time in ms until first possible grant 16 | uint64_t tbm_withdraw(token_bucket_meter_t *tb, double amount, uint64_t now); 17 | 18 | void tbm_init(token_bucket_meter_t *tb, double rate, double burst); 19 | 20 | -------------------------------------------------------------------------------- /tcp.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andoma/libsvc/30e9b790016bf64c753f53a151e48cc2853a8395/tcp.c -------------------------------------------------------------------------------- /tcp.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andoma/libsvc/30e9b790016bf64c753f53a151e48cc2853a8395/tcp.h -------------------------------------------------------------------------------- /threading.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | void set_thread_namef(const char *fmt, ...); 29 | 30 | extern void mutex_unlock_ptr(pthread_mutex_t **p); 31 | 32 | #define scoped_lock(x) \ 33 | pthread_mutex_t *scopedmutex__ ## __LINE__ \ 34 | __attribute__((cleanup(mutex_unlock_ptr))) = x; \ 35 | pthread_mutex_lock(x); 36 | -------------------------------------------------------------------------------- /trace.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #define COLOR_OFF "\017" 31 | #define COLOR_BLUE "\00302" 32 | #define COLOR_GREEN "\00303" 33 | #define COLOR_RED "\00304" 34 | #define COLOR_BROWN "\00305" 35 | #define COLOR_PURPLE "\00306" 36 | #define COLOR_ORANGE "\00307" 37 | #define COLOR_YELLOW "\00308" 38 | 39 | void decolorize(char *str); 40 | void trace(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); 41 | void tracev(int level, const char *fmt, va_list ap); 42 | 43 | 44 | void enable_syslog(const char *program, const char *facility); 45 | 46 | void hexdump(const char *pfx, const void *data_, int len); 47 | 48 | void trace_enable_stdout(void); 49 | 50 | void trace_set_outputs(int to_stdout, int to_stderr); 51 | 52 | void trace_set_callback(void (*cb)(int level, const char *msg)); 53 | 54 | 55 | typedef struct xlog_kv { 56 | union { 57 | const char *key; 58 | const struct xlog_kv *next; 59 | }; 60 | union { 61 | const char *value_str; 62 | int64_t value_int; 63 | }; 64 | enum { 65 | XLOG_TYPE_STRING, 66 | XLOG_TYPE_INT, 67 | XLOG_TYPE_LINK, 68 | } type; 69 | } xlog_kv_t; 70 | 71 | 72 | 73 | static inline const xlog_kv_t 74 | XLOG_STR(const char *key, const char *value) 75 | { 76 | return (const xlog_kv_t){.key = key, .value_str = value, 77 | .type = XLOG_TYPE_STRING}; 78 | } 79 | 80 | static inline const xlog_kv_t 81 | XLOG_INT(const char *key, int64_t value) 82 | { 83 | return (const xlog_kv_t){.key = key, .value_int = value, 84 | .type = XLOG_TYPE_INT}; 85 | } 86 | 87 | static inline const xlog_kv_t 88 | XLOG_LINK(const xlog_kv_t *kv) 89 | { 90 | return (const xlog_kv_t){.next = kv, .type = XLOG_TYPE_LINK}; 91 | } 92 | 93 | #define XLOGS(x...) (const xlog_kv_t []){x, { .key = NULL}} 94 | 95 | void xlog(int level, const xlog_kv_t *kv, const char *fmt, ...) 96 | __attribute__ ((format (printf, 3, 4))); 97 | 98 | void trace_enable_builtin_syslog(const char *host, int port, 99 | const char *format, int tls, 100 | const char *hostname, 101 | int wait_for_connection); 102 | -------------------------------------------------------------------------------- /trap.h: -------------------------------------------------------------------------------- 1 | void trap_init(void (*crashmsg)(const char *str), char *argv0); 2 | -------------------------------------------------------------------------------- /utf8.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "utf8.h" 29 | 30 | /** 31 | * 32 | */ 33 | int 34 | utf8_put(char *out, int c) 35 | { 36 | if(c == 0xfffe || c == 0xffff || (c >= 0xD800 && c < 0xE000)) 37 | return 0; 38 | 39 | if (c < 0x80) { 40 | if(out) 41 | *out = c; 42 | return 1; 43 | } 44 | 45 | if(c < 0x800) { 46 | if(out) { 47 | *out++ = 0xc0 | (0x1f & (c >> 6)); 48 | *out = 0x80 | (0x3f & c); 49 | } 50 | return 2; 51 | } 52 | 53 | if(c < 0x10000) { 54 | if(out) { 55 | *out++ = 0xe0 | (0x0f & (c >> 12)); 56 | *out++ = 0x80 | (0x3f & (c >> 6)); 57 | *out = 0x80 | (0x3f & c); 58 | } 59 | return 3; 60 | } 61 | 62 | if(c < 0x200000) { 63 | if(out) { 64 | *out++ = 0xf0 | (0x07 & (c >> 18)); 65 | *out++ = 0x80 | (0x3f & (c >> 12)); 66 | *out++ = 0x80 | (0x3f & (c >> 6)); 67 | *out = 0x80 | (0x3f & c); 68 | } 69 | return 4; 70 | } 71 | 72 | if(c < 0x4000000) { 73 | if(out) { 74 | *out++ = 0xf8 | (0x03 & (c >> 24)); 75 | *out++ = 0x80 | (0x3f & (c >> 18)); 76 | *out++ = 0x80 | (0x3f & (c >> 12)); 77 | *out++ = 0x80 | (0x3f & (c >> 6)); 78 | *out++ = 0x80 | (0x3f & c); 79 | } 80 | return 5; 81 | } 82 | 83 | if(out) { 84 | *out++ = 0xfc | (0x01 & (c >> 30)); 85 | *out++ = 0x80 | (0x3f & (c >> 24)); 86 | *out++ = 0x80 | (0x3f & (c >> 18)); 87 | *out++ = 0x80 | (0x3f & (c >> 12)); 88 | *out++ = 0x80 | (0x3f & (c >> 6)); 89 | *out++ = 0x80 | (0x3f & c); 90 | } 91 | return 6; 92 | } 93 | 94 | 95 | /** 96 | * Strict error checking UTF-8 decoder. 97 | * Based on the wikipedia article http://en.wikipedia.org/wiki/UTF-8 98 | * Checks for these errors: 99 | * 100 | * - Bytes 192, 193 and 245 - 255 must never appear. 101 | * 102 | * - Unexpected continuation byte. 103 | * 104 | * - Start byte not followed by enough continuation bytes. 105 | * 106 | * - A sequence that decodes to a value that should use a shorter 107 | * sequence (an "overlong form"). 108 | * 109 | */ 110 | int 111 | utf8_get(const char **s, const char *stop) 112 | { 113 | uint8_t c; 114 | int r, l, m; 115 | 116 | if(*s == stop) 117 | return 0xfffd; 118 | 119 | c = **s; 120 | *s = *s + 1; 121 | 122 | switch(c) { 123 | case 0 ... 127: 124 | return c; 125 | 126 | case 194 ... 223: 127 | r = c & 0x1f; 128 | l = 1; 129 | m = 0x80; 130 | break; 131 | 132 | case 224 ... 239: 133 | r = c & 0xf; 134 | l = 2; 135 | m = 0x800; 136 | break; 137 | 138 | case 240 ... 247: 139 | r = c & 0x7; 140 | l = 3; 141 | m = 0x10000; 142 | break; 143 | 144 | case 248 ... 251: 145 | r = c & 0x3; 146 | l = 4; 147 | m = 0x200000; 148 | break; 149 | 150 | case 252 ... 253: 151 | r = c & 0x1; 152 | l = 5; 153 | m = 0x4000000; 154 | break; 155 | default: 156 | return 0xfffd; 157 | } 158 | 159 | while(l-- > 0) { 160 | if(*s == stop) 161 | return 0xfffd; 162 | c = **s; 163 | if((c & 0xc0) != 0x80) 164 | return 0xfffd; 165 | *s = *s + 1; 166 | r = r << 6 | (c & 0x3f); 167 | } 168 | if(r < m) 169 | return 0xfffd; // overlong sequence 170 | 171 | return r; 172 | } 173 | 174 | /** 175 | * 176 | */ 177 | int 178 | utf8_len(const char *s) 179 | { 180 | int l = 0; 181 | while(utf8_get(&s, NULL)) 182 | l++; 183 | return l; 184 | } 185 | 186 | 187 | 188 | /** 189 | * 190 | */ 191 | char * 192 | utf8_cleanup(const char *str) 193 | { 194 | const char *s = str; 195 | int outlen = 1; 196 | int c; 197 | int bad = 0; 198 | while((c = utf8_get(&s, NULL)) != 0) { 199 | if(c == 0xfffd) 200 | bad = 1; 201 | outlen += utf8_put(NULL, c); 202 | } 203 | 204 | if(!bad) 205 | return NULL; 206 | 207 | char *out = malloc(outlen); 208 | char *ret = out; 209 | while((c = utf8_get(&str, NULL)) != 0) 210 | out += utf8_put(out, c); 211 | 212 | *out = 0; 213 | return ret; 214 | } 215 | 216 | 217 | /** 218 | * 219 | */ 220 | void 221 | utf8_cleanup_inplace(char *str, size_t len) 222 | { 223 | const char *s = str; 224 | int outlen = 1; 225 | int c; 226 | int bad = 0; 227 | while((c = utf8_get(&s, NULL)) != 0) { 228 | if(c == 0xfffd) 229 | bad = 1; 230 | outlen += utf8_put(NULL, c); 231 | } 232 | 233 | if(!bad) 234 | return; 235 | 236 | char *out = alloca(outlen); 237 | const char *ret = out; 238 | s = str; 239 | while((c = utf8_get(&s, NULL)) != 0) 240 | out += utf8_put(out, c); 241 | 242 | *out = 0; 243 | 244 | snprintf(str, len, "%s", ret); 245 | } 246 | 247 | 248 | 249 | /** 250 | * Return 1 iff the string is UTF-8 conformant 251 | */ 252 | int 253 | utf8_verify(const char *str, const char *end) 254 | { 255 | int c; 256 | 257 | while(str != end && (c = utf8_get(&str, end)) != 0) { 258 | if(c == 0xfffd) 259 | return 0; 260 | } 261 | return 1; 262 | } 263 | -------------------------------------------------------------------------------- /utf8.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | int utf8_put(char *out, int c); 27 | 28 | int utf8_get(const char **s, const char *stop); 29 | 30 | char *utf8_cleanup(const char *str); 31 | 32 | void utf8_cleanup_inplace(char *str, size_t len); 33 | 34 | int utf8_len(const char *s); 35 | 36 | int utf8_verify(const char *str, const char *end); 37 | -------------------------------------------------------------------------------- /vec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define VEC_HEAD(name, type) struct name { \ 4 | type *vh_p; \ 5 | size_t vh_length; \ 6 | size_t vh_capacity; \ 7 | } 8 | 9 | #define VEC_ITEM(head, n) (head)->vh_p[n] 10 | 11 | #define VEC_LEN(head) ((head)->vh_length) 12 | 13 | #define VEC_RESIZE(head, n) do { \ 14 | if(n > (head)->vh_capacity) { \ 15 | (head)->vh_capacity = (n) * 2; \ 16 | size_t memsiz = (head)->vh_capacity * \ 17 | sizeof(typeof((head)->vh_p[0])); \ 18 | (head)->vh_p = realloc((head)->vh_p, memsiz); \ 19 | } \ 20 | (head)->vh_length = n; \ 21 | } while(0) 22 | 23 | #define VEC_SET_CAPACITY(head, n) do { \ 24 | if(n > (head)->vh_capacity) { \ 25 | (head)->vh_capacity = (n); \ 26 | size_t memsiz = (head)->vh_capacity * \ 27 | sizeof(typeof((head)->vh_p[0])); \ 28 | (head)->vh_p = realloc((head)->vh_p, memsiz); \ 29 | } \ 30 | } while(0) 31 | 32 | #define VEC_PUSH_BACK(head, item) do { \ 33 | size_t cursize = VEC_LEN(head); \ 34 | VEC_RESIZE(head, cursize + 1); \ 35 | VEC_ITEM(head, cursize) = (item); \ 36 | } while(0) 37 | 38 | #define VEC_POP(head) (head)->vh_length-- 39 | 40 | #define VEC_CLEAR(head) do { \ 41 | (head)->vh_capacity = 0; \ 42 | (head)->vh_length = 0; \ 43 | free((head)->vh_p); \ 44 | } while(0) 45 | 46 | 47 | #define VEC_SORT(head, cmpfun) \ 48 | qsort((head)->vh_p, (head)->vh_length, \ 49 | sizeof((head)->vh_p[0]), (void *)cmpfun) 50 | -------------------------------------------------------------------------------- /websocket.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | #include 24 | 25 | #include "mbuf.h" 26 | #include "websocket.h" 27 | #include "bytestream.h" 28 | 29 | 30 | /** 31 | * 32 | */ 33 | void 34 | websocket_free(websocket_state_t *state) 35 | { 36 | free(state->packet); 37 | state->packet = NULL; 38 | } 39 | 40 | /** 41 | * 42 | */ 43 | int 44 | websocket_build_hdr(uint8_t hdr[static WEBSOCKET_MAX_HDR_LEN], int opcode, size_t len, int compressed) 45 | { 46 | int hlen; 47 | hdr[0] = 0x80 | (opcode & 0xf) | (compressed ? 0x40 : 0); 48 | if(len <= 125) { 49 | hdr[1] = len; 50 | hlen = 2; 51 | } else if(len < 65536) { 52 | hdr[1] = 126; 53 | hdr[2] = len >> 8; 54 | hdr[3] = len; 55 | hlen = 4; 56 | } else { 57 | hdr[1] = 127; 58 | wr64_be(hdr + 2, len); 59 | hlen = 10; 60 | } 61 | return hlen; 62 | } 63 | 64 | 65 | 66 | /** 67 | * 68 | */ 69 | void 70 | websocket_append_hdr(mbuf_t *q, int opcode, size_t len) 71 | { 72 | uint8_t hdr[14]; // max header length 73 | const int hlen = websocket_build_hdr(hdr, opcode, len, 0); 74 | mbuf_append(q, hdr, hlen); 75 | } 76 | 77 | 78 | /** 79 | * 80 | */ 81 | int 82 | websocket_parse(mbuf_t *q, 83 | int (*cb)(void *opaque, int opcode, uint8_t **data, int len, 84 | int flags), 85 | void *opaque, websocket_state_t *ws) 86 | { 87 | uint8_t hdr[14]; // max header length 88 | while(1) { 89 | int p = mbuf_peek(q, &hdr, 14); 90 | const uint8_t *m; 91 | 92 | if(p < 2) 93 | return 0; 94 | const uint8_t fin = hdr[0] & 0x80; 95 | const uint8_t compressed = hdr[0] & 0x40; 96 | const int opcode = hdr[0] & 0xf; 97 | int64_t len = hdr[1] & 0x7f; 98 | int hoff = 2; 99 | if(len == 126) { 100 | if(p < 4) 101 | return 0; 102 | len = hdr[2] << 8 | hdr[3]; 103 | hoff = 4; 104 | } else if(len == 127) { 105 | if(p < 10) 106 | return 0; 107 | len = rd64_be(hdr + 2); 108 | hoff = 10; 109 | } 110 | 111 | if(hdr[1] & 0x80) { 112 | if(p < hoff + 4) 113 | return 0; 114 | m = hdr + hoff; 115 | 116 | hoff += 4; 117 | } else { 118 | m = NULL; 119 | } 120 | 121 | if(q->mq_size < hoff + len) 122 | return 0; 123 | 124 | mbuf_drop(q, hoff); 125 | 126 | if(opcode & 0x8) { 127 | // Ctrl frame 128 | uint8_t *p = malloc(len + 1); 129 | if(p == NULL) 130 | return 1; 131 | 132 | mbuf_read(q, p, len); 133 | if(m != NULL) for(int i = 0; i < len; i++) p[i] ^= m[i&3]; 134 | 135 | p[len] = 0; // Trailing zero for convenience 136 | int err = cb(opaque, opcode, &p, len, 0); 137 | free(p); 138 | if(!err) 139 | continue; 140 | return 1; 141 | } 142 | 143 | // The four extra pad bytes are used for: 144 | // pad[0] is always 0 so the websocket payload can be parsed as a string 145 | // or 146 | // All four bytes can be used to append the deflate flush trailer 147 | void *r = realloc(ws->packet, ws->packet_size + len + 4); 148 | if(r == NULL) 149 | return 1; 150 | ws->packet = r; 151 | 152 | uint8_t *d = ws->packet + ws->packet_size; 153 | d[len] = 0; 154 | mbuf_read(q, d, len); 155 | 156 | if(m != NULL) for(int i = 0; i < len; i++) d[i] ^= m[i&3]; 157 | 158 | if(opcode != 0) { 159 | ws->opcode = opcode; 160 | if(compressed) { 161 | ws->flags |= WS_MESSAGE_COMPRESSED; 162 | } 163 | } 164 | 165 | ws->packet_size += len; 166 | 167 | if(!fin) 168 | continue; 169 | 170 | int err = cb(opaque, ws->opcode, &ws->packet, ws->packet_size, ws->flags); 171 | ws->packet_size = 0; 172 | ws->flags = 0; 173 | if(!err) 174 | continue; 175 | return 1; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /websocket.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (C) 2008 - 2014 Andreas Öman 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ******************************************************************************/ 23 | 24 | #pragma once 25 | 26 | #include 27 | 28 | struct mbuf; 29 | 30 | typedef struct websocket_state { 31 | uint8_t *packet; 32 | int packet_size; 33 | uint8_t opcode; 34 | uint8_t flags; 35 | #define WS_MESSAGE_COMPRESSED 0x1 36 | } websocket_state_t; 37 | 38 | #define WEBSOCKET_MAX_HDR_LEN 14 39 | 40 | int websocket_build_hdr(uint8_t hdr[static WEBSOCKET_MAX_HDR_LEN], 41 | int opcode, size_t len, int compressed); 42 | 43 | void websocket_append_hdr(struct mbuf *q, int opcode, size_t len); 44 | 45 | void websocket_free(websocket_state_t *state); 46 | 47 | /** 48 | * Return-values 49 | * 0 - Not enough data in input buffer, call again when more is available 50 | * 1 - Fatal error, disconnect 51 | */ 52 | int websocket_parse(struct mbuf *q, 53 | int (*cb)(void *opaque, int opcode, 54 | uint8_t **data, int len, int flags), 55 | void *opaque, websocket_state_t *state); 56 | 57 | 58 | #define WS_OPCODE_CLOSE 8 59 | #define WS_OPCODE_PING 9 60 | #define WS_OPCODE_PONG 10 61 | 62 | #define WS_STATUS_NORMAL_CLOSE 1000 63 | #define WS_STATUS_GOING_AWAY 1001 64 | #define WS_STATUS_PROTOCOL_ERROR 1002 65 | #define WS_STATUS_CANNOT_ACCEPT 1003 66 | 67 | #define WS_STATUS_NO_STATUS 1005 68 | #define WS_STATUS_ABNORMALLY_CLOSED 1006 69 | -------------------------------------------------------------------------------- /websocket_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "tcp.h" 3 | 4 | typedef struct ws_client ws_client_t; 5 | 6 | #define WSC_DEBUG 0x1 7 | 8 | enum { 9 | WSC_TAG_END, 10 | WSC_TAG_FLAGS, 11 | WSC_TAG_AUTH, 12 | WSC_TAG_TIMEOUT, 13 | WSC_TAG_USERNPASS, 14 | WSC_TAG_URL, 15 | WSC_TAG_HOSTPORTPATH, 16 | WSC_TAG_PROTOCOL, 17 | }; 18 | 19 | 20 | #define WSC_FLAGS(a) WSC_TAG_FLAGS, a 21 | #define WSC_AUTH(a) WSC_TAG_AUTH, a 22 | #define WSC_TIMEOUT(a) WSC_TAG_TIMEOUT, a 23 | #define WSC_USERNPASS(a, b) WSC_TAG_USERNPASS, a, b 24 | #define WSC_URL(a) WSC_TAG_URL, a 25 | #define WSC_HOSTPORTPATH(a, b, c) WSC_TAG_HOSTPORTPATH, a, (int)b, c 26 | #define WSC_SSL(a) WSC_TAG_SSL, a 27 | #define WSC_PROTOCOL(a) WSC_TAG_PROTOCOL, a 28 | 29 | typedef void (wsc_fn_t)(void *opaque, int opcode, 30 | const void *buf, size_t len); 31 | 32 | ws_client_t *ws_client_create(wsc_fn_t *fn, void *opaque, ...) 33 | __attribute__((__sentinel__(0))); 34 | 35 | 36 | int ws_client_send(ws_client_t *wsc, int opcode, 37 | const void *data, size_t len); 38 | 39 | int ws_client_sendq(ws_client_t *wsc, int opcode, mbuf_t *mq); 40 | 41 | void ws_client_send_close(ws_client_t *wsc, int code, const char *msg); 42 | 43 | void ws_client_start(ws_client_t *wsc); 44 | 45 | void ws_client_destroy(ws_client_t *wsc); 46 | 47 | const char *ws_client_get_hostname(ws_client_t *wsc); 48 | 49 | char *ws_client_get_protocol(ws_client_t *wsc); 50 | --------------------------------------------------------------------------------