libevent-2.1.13-stable/0000755000175000017500000000000015221201660013675 5ustar00nickmnickmlibevent-2.1.13-stable/bufferevent-internal.h0000644000175000017500000004607615221012604020206 0ustar00nickmnickm/* * Copyright (c) 2008-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef BUFFEREVENT_INTERNAL_H_INCLUDED_ #define BUFFEREVENT_INTERNAL_H_INCLUDED_ #ifdef __cplusplus extern "C" { #endif #include "event2/event-config.h" #include "event2/event_struct.h" #include "evconfig-private.h" #include "event2/util.h" #include "util-internal.h" #include "defer-internal.h" #include "evthread-internal.h" #include "event2/thread.h" #include "ratelim-internal.h" #include "event2/bufferevent_struct.h" #include "ipv6-internal.h" #ifdef _WIN32 #include #endif #ifdef EVENT__HAVE_NETINET_IN_H #include #endif #ifdef EVENT__HAVE_NETINET_IN6_H #include #endif /* These flags are reasons that we might be declining to actually enable reading or writing on a bufferevent. */ /* On a all bufferevents, for reading: used when we have read up to the watermark value. On a filtering bufferevent, for writing: used when the underlying bufferevent's write buffer has been filled up to its watermark value. */ #define BEV_SUSPEND_WM 0x01 /* On a base bufferevent: when we have emptied a bandwidth buckets */ #define BEV_SUSPEND_BW 0x02 /* On a base bufferevent: when we have emptied the group's bandwidth bucket. */ #define BEV_SUSPEND_BW_GROUP 0x04 /* On a socket bufferevent: can't do any operations while we're waiting for * name lookup to finish. */ #define BEV_SUSPEND_LOOKUP 0x08 /* On a base bufferevent, for reading: used when a filter has choked this * (underlying) bufferevent because it has stopped reading from it. */ #define BEV_SUSPEND_FILT_READ 0x10 typedef ev_uint16_t bufferevent_suspend_flags; struct bufferevent_rate_limit_group { /** List of all members in the group */ LIST_HEAD(rlim_group_member_list, bufferevent_private) members; /** Current limits for the group. */ struct ev_token_bucket rate_limit; struct ev_token_bucket_cfg rate_limit_cfg; /** True iff we don't want to read from any member of the group.until * the token bucket refills. */ unsigned read_suspended : 1; /** True iff we don't want to write from any member of the group.until * the token bucket refills. */ unsigned write_suspended : 1; /** True iff we were unable to suspend one of the bufferevents in the * group for reading the last time we tried, and we should try * again. */ unsigned pending_unsuspend_read : 1; /** True iff we were unable to suspend one of the bufferevents in the * group for writing the last time we tried, and we should try * again. */ unsigned pending_unsuspend_write : 1; /*@{*/ /** Total number of bytes read or written in this group since last * reset. */ ev_uint64_t total_read; ev_uint64_t total_written; /*@}*/ /** The number of bufferevents in the group. */ int n_members; /** The smallest number of bytes that any member of the group should * be limited to read or write at a time. */ ev_ssize_t min_share; ev_ssize_t configured_min_share; /** Timeout event that goes off once a tick, when the bucket is ready * to refill. */ struct event master_refill_event; /** Seed for weak random number generator. Protected by 'lock' */ struct evutil_weakrand_state weakrand_seed; /** Lock to protect the members of this group. This lock should nest * within every bufferevent lock: if you are holding this lock, do * not assume you can lock another bufferevent. */ void *lock; }; /** Fields for rate-limiting a single bufferevent. */ struct bufferevent_rate_limit { /* Linked-list elements for storing this bufferevent_private in a * group. * * Note that this field is supposed to be protected by the group * lock */ LIST_ENTRY(bufferevent_private) next_in_group; /** The rate-limiting group for this bufferevent, or NULL if it is * only rate-limited on its own. */ struct bufferevent_rate_limit_group *group; /* This bufferevent's current limits. */ struct ev_token_bucket limit; /* Pointer to the rate-limit configuration for this bufferevent. * Can be shared. XXX reference-count this? */ struct ev_token_bucket_cfg *cfg; /* Timeout event used when one this bufferevent's buckets are * empty. */ struct event refill_bucket_event; }; /** Parts of the bufferevent structure that are shared among all bufferevent * types, but not exposed in bufferevent_struct.h. */ struct bufferevent_private { /** The underlying bufferevent structure. */ struct bufferevent bev; /** Evbuffer callback to enforce watermarks on input. */ struct evbuffer_cb_entry *read_watermarks_cb; /** If set, we should free the lock when we free the bufferevent. */ unsigned own_lock : 1; /** Flag: set if we have deferred callbacks and a read callback is * pending. */ unsigned readcb_pending : 1; /** Flag: set if we have deferred callbacks and a write callback is * pending. */ unsigned writecb_pending : 1; /** Flag: set if we are currently busy connecting. */ unsigned connecting : 1; /** Flag: set if a connect failed prematurely; this is a hack for * getting around the bufferevent abstraction. */ unsigned connection_refused : 1; /** Set to the events pending if we have deferred callbacks and * an events callback is pending. */ short eventcb_pending; /** If set, read is suspended until one or more conditions are over. * The actual value here is a bitfield of those conditions; see the * BEV_SUSPEND_* flags above. */ bufferevent_suspend_flags read_suspended; /** If set, writing is suspended until one or more conditions are over. * The actual value here is a bitfield of those conditions; see the * BEV_SUSPEND_* flags above. */ bufferevent_suspend_flags write_suspended; /** Set to the current socket errno if we have deferred callbacks and * an events callback is pending. */ int errno_pending; /** The DNS error code for bufferevent_socket_connect_hostname */ int dns_error; /** Used to implement deferred callbacks */ struct event_callback deferred; /** The options this bufferevent was constructed with */ enum bufferevent_options options; /** Current reference count for this bufferevent. */ int refcnt; /** Lock for this bufferevent. Shared by the inbuf and the outbuf. * If NULL, locking is disabled. */ void *lock; /** No matter how big our bucket gets, don't try to read more than this * much in a single read operation. */ ev_ssize_t max_single_read; /** No matter how big our bucket gets, don't try to write more than this * much in a single write operation. */ ev_ssize_t max_single_write; /** Rate-limiting information for this bufferevent */ struct bufferevent_rate_limit *rate_limiting; /* Saved conn_addr, to extract IP address from it. * * Because some servers may reset/close connection without waiting clients, * in that case we can't extract IP address even in close_cb. * So we need to save it, just after we connected to remote server, or * after resolving (to avoid extra dns requests during retrying, since UDP * is slow) */ /* NOTE: it might be nice to use fewer bytes here, but we need to store * sockaddr_un sometimes in order to make AF_UNIX sockets work as expected * with http.c. */ struct sockaddr_storage conn_address; struct evdns_getaddrinfo_request *dns_request; }; /** Possible operations for a control callback. */ enum bufferevent_ctrl_op { BEV_CTRL_SET_FD, BEV_CTRL_GET_FD, BEV_CTRL_GET_UNDERLYING, BEV_CTRL_CANCEL_ALL }; /** Possible data types for a control callback */ union bufferevent_ctrl_data { void *ptr; evutil_socket_t fd; }; /** Implementation table for a bufferevent: holds function pointers and other information to make the various bufferevent types work. */ struct bufferevent_ops { /** The name of the bufferevent's type. */ const char *type; /** At what offset into the implementation type will we find a bufferevent structure? Example: if the type is implemented as struct bufferevent_x { int extra_data; struct bufferevent bev; } then mem_offset should be offsetof(struct bufferevent_x, bev) */ off_t mem_offset; /** Enables one or more of EV_READ|EV_WRITE on a bufferevent. Does not need to adjust the 'enabled' field. Returns 0 on success, -1 on failure. */ int (*enable)(struct bufferevent *, short); /** Disables one or more of EV_READ|EV_WRITE on a bufferevent. Does not need to adjust the 'enabled' field. Returns 0 on success, -1 on failure. */ int (*disable)(struct bufferevent *, short); /** Detatches the bufferevent from related data structures. Called as * soon as its reference count reaches 0. */ void (*unlink)(struct bufferevent *); /** Free any storage and deallocate any extra data or structures used in this implementation. Called when the bufferevent is finalized. */ void (*destruct)(struct bufferevent *); /** Called when the timeouts on the bufferevent have changed.*/ int (*adj_timeouts)(struct bufferevent *); /** Called to flush data. */ int (*flush)(struct bufferevent *, short, enum bufferevent_flush_mode); /** Called to access miscellaneous fields. */ int (*ctrl)(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); }; extern const struct bufferevent_ops bufferevent_ops_socket; extern const struct bufferevent_ops bufferevent_ops_filter; extern const struct bufferevent_ops bufferevent_ops_pair; #define BEV_IS_SOCKET(bevp) ((bevp)->be_ops == &bufferevent_ops_socket) #define BEV_IS_FILTER(bevp) ((bevp)->be_ops == &bufferevent_ops_filter) #define BEV_IS_PAIR(bevp) ((bevp)->be_ops == &bufferevent_ops_pair) #if defined(EVENT__HAVE_OPENSSL) extern const struct bufferevent_ops bufferevent_ops_openssl; #define BEV_IS_OPENSSL(bevp) ((bevp)->be_ops == &bufferevent_ops_openssl) #else #define BEV_IS_OPENSSL(bevp) 0 #endif #ifdef _WIN32 extern const struct bufferevent_ops bufferevent_ops_async; #define BEV_IS_ASYNC(bevp) ((bevp)->be_ops == &bufferevent_ops_async) #else #define BEV_IS_ASYNC(bevp) 0 #endif /** Initialize the shared parts of a bufferevent. */ EVENT2_EXPORT_SYMBOL int bufferevent_init_common_(struct bufferevent_private *, struct event_base *, const struct bufferevent_ops *, enum bufferevent_options options); /** For internal use: temporarily stop all reads on bufev, until the conditions * in 'what' are over. */ EVENT2_EXPORT_SYMBOL void bufferevent_suspend_read_(struct bufferevent *bufev, bufferevent_suspend_flags what); /** For internal use: clear the conditions 'what' on bufev, and re-enable * reading if there are no conditions left. */ EVENT2_EXPORT_SYMBOL void bufferevent_unsuspend_read_(struct bufferevent *bufev, bufferevent_suspend_flags what); /** For internal use: temporarily stop all writes on bufev, until the conditions * in 'what' are over. */ void bufferevent_suspend_write_(struct bufferevent *bufev, bufferevent_suspend_flags what); /** For internal use: clear the conditions 'what' on bufev, and re-enable * writing if there are no conditions left. */ void bufferevent_unsuspend_write_(struct bufferevent *bufev, bufferevent_suspend_flags what); #define bufferevent_wm_suspend_read(b) \ bufferevent_suspend_read_((b), BEV_SUSPEND_WM) #define bufferevent_wm_unsuspend_read(b) \ bufferevent_unsuspend_read_((b), BEV_SUSPEND_WM) /* Disable a bufferevent. Equivalent to bufferevent_disable(), but first resets 'connecting' flag to force EV_WRITE down for sure. XXXX this method will go away in the future; try not to add new users. See comment in evhttp_connection_reset_() for discussion. @param bufev the bufferevent to be disabled @param event any combination of EV_READ | EV_WRITE. @return 0 if successful, or -1 if an error occurred @see bufferevent_disable() */ EVENT2_EXPORT_SYMBOL int bufferevent_disable_hard_(struct bufferevent *bufev, short event); /** Internal: Set up locking on a bufferevent. If lock is set, use it. * Otherwise, use a new lock. */ EVENT2_EXPORT_SYMBOL int bufferevent_enable_locking_(struct bufferevent *bufev, void *lock); /** Internal: backwards compat macro for the now public function * Increment the reference count on bufev. */ #define bufferevent_incref_(bufev) bufferevent_incref(bufev) /** Internal: Lock bufev and increase its reference count. * unlocking it otherwise. */ EVENT2_EXPORT_SYMBOL void bufferevent_incref_and_lock_(struct bufferevent *bufev); /** Internal: backwards compat macro for the now public function * Decrement the reference count on bufev. Returns 1 if it freed * the bufferevent.*/ #define bufferevent_decref_(bufev) bufferevent_decref(bufev) /** Internal: Drop the reference count on bufev, freeing as necessary, and * unlocking it otherwise. Returns 1 if it freed the bufferevent. */ EVENT2_EXPORT_SYMBOL int bufferevent_decref_and_unlock_(struct bufferevent *bufev); /** Internal: If callbacks are deferred and we have a read callback, schedule * a readcb. Otherwise just run the readcb. Ignores watermarks. */ EVENT2_EXPORT_SYMBOL void bufferevent_run_readcb_(struct bufferevent *bufev, int options); /** Internal: If callbacks are deferred and we have a write callback, schedule * a writecb. Otherwise just run the writecb. Ignores watermarks. */ EVENT2_EXPORT_SYMBOL void bufferevent_run_writecb_(struct bufferevent *bufev, int options); /** Internal: If callbacks are deferred and we have an eventcb, schedule * it to run with events "what". Otherwise just run the eventcb. * See bufferevent_trigger_event for meaning of "options". */ EVENT2_EXPORT_SYMBOL void bufferevent_run_eventcb_(struct bufferevent *bufev, short what, int options); /** Internal: Run or schedule (if deferred or options contain * BEV_TRIG_DEFER_CALLBACKS) I/O callbacks specified in iotype. * Must already hold the bufev lock. Honors watermarks unless * BEV_TRIG_IGNORE_WATERMARKS is in options. */ static inline void bufferevent_trigger_nolock_(struct bufferevent *bufev, short iotype, int options); /* Making this inline since all of the common-case calls to this function in * libevent use constant arguments. */ static inline void bufferevent_trigger_nolock_(struct bufferevent *bufev, short iotype, int options) { if ((iotype & EV_READ) && ((options & BEV_TRIG_IGNORE_WATERMARKS) || evbuffer_get_length(bufev->input) >= bufev->wm_read.low)) bufferevent_run_readcb_(bufev, options); if ((iotype & EV_WRITE) && ((options & BEV_TRIG_IGNORE_WATERMARKS) || evbuffer_get_length(bufev->output) <= bufev->wm_write.low)) bufferevent_run_writecb_(bufev, options); } /** Internal: Add the event 'ev' with timeout tv, unless tv is set to 0, in * which case add ev with no timeout. */ EVENT2_EXPORT_SYMBOL int bufferevent_add_event_(struct event *ev, const struct timeval *tv); /* ========= * These next functions implement timeouts for bufferevents that aren't doing * anything else with ev_read and ev_write, to handle timeouts. * ========= */ /** Internal use: Set up the ev_read and ev_write callbacks so that * the other "generic_timeout" functions will work on it. Call this from * the constructor function. */ EVENT2_EXPORT_SYMBOL void bufferevent_init_generic_timeout_cbs_(struct bufferevent *bev); /** Internal use: Add or delete the generic timeout events as appropriate. * (If an event is enabled and a timeout is set, we add the event. Otherwise * we delete it.) Call this from anything that changes the timeout values, * that enabled EV_READ or EV_WRITE, or that disables EV_READ or EV_WRITE. */ EVENT2_EXPORT_SYMBOL int bufferevent_generic_adj_timeouts_(struct bufferevent *bev); EVENT2_EXPORT_SYMBOL int bufferevent_generic_adj_existing_timeouts_(struct bufferevent *bev); EVENT2_EXPORT_SYMBOL enum bufferevent_options bufferevent_get_options_(struct bufferevent *bev); EVENT2_EXPORT_SYMBOL const struct sockaddr* bufferevent_socket_get_conn_address_(struct bufferevent *bev); EVENT2_EXPORT_SYMBOL void bufferevent_socket_set_conn_address_fd_(struct bufferevent *bev, evutil_socket_t fd); EVENT2_EXPORT_SYMBOL int bufferevent_socket_set_conn_address_(struct bufferevent *bev, struct sockaddr *addr, size_t addrlen); /** Internal use: We have just successfully read data into an inbuf, so * reset the read timeout (if any). */ #define BEV_RESET_GENERIC_READ_TIMEOUT(bev) \ do { \ if (evutil_timerisset(&(bev)->timeout_read)) \ event_add(&(bev)->ev_read, &(bev)->timeout_read); \ } while (0) /** Internal use: We have just successfully written data from an inbuf, so * reset the read timeout (if any). */ #define BEV_RESET_GENERIC_WRITE_TIMEOUT(bev) \ do { \ if (evutil_timerisset(&(bev)->timeout_write)) \ event_add(&(bev)->ev_write, &(bev)->timeout_write); \ } while (0) #define BEV_DEL_GENERIC_READ_TIMEOUT(bev) \ event_del(&(bev)->ev_read) #define BEV_DEL_GENERIC_WRITE_TIMEOUT(bev) \ event_del(&(bev)->ev_write) /** Internal: Given a bufferevent, return its corresponding * bufferevent_private. */ #define BEV_UPCAST(b) EVUTIL_UPCAST((b), struct bufferevent_private, bev) #ifdef EVENT__DISABLE_THREAD_SUPPORT #define BEV_LOCK(b) EVUTIL_NIL_STMT_ #define BEV_UNLOCK(b) EVUTIL_NIL_STMT_ #else /** Internal: Grab the lock (if any) on a bufferevent */ #define BEV_LOCK(b) do { \ struct bufferevent_private *locking = BEV_UPCAST(b); \ EVLOCK_LOCK(locking->lock, 0); \ } while (0) /** Internal: Release the lock (if any) on a bufferevent */ #define BEV_UNLOCK(b) do { \ struct bufferevent_private *locking = BEV_UPCAST(b); \ EVLOCK_UNLOCK(locking->lock, 0); \ } while (0) #endif /* ==== For rate-limiting. */ EVENT2_EXPORT_SYMBOL int bufferevent_decrement_write_buckets_(struct bufferevent_private *bev, ev_ssize_t bytes); EVENT2_EXPORT_SYMBOL int bufferevent_decrement_read_buckets_(struct bufferevent_private *bev, ev_ssize_t bytes); EVENT2_EXPORT_SYMBOL ev_ssize_t bufferevent_get_read_max_(struct bufferevent_private *bev); EVENT2_EXPORT_SYMBOL ev_ssize_t bufferevent_get_write_max_(struct bufferevent_private *bev); int bufferevent_ratelim_init_(struct bufferevent_private *bev); #ifdef __cplusplus } #endif #endif /* BUFFEREVENT_INTERNAL_H_INCLUDED_ */ libevent-2.1.13-stable/event_iocp.c0000644000175000017500000001703215215775075016222 0ustar00nickmnickm/* * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "evconfig-private.h" #ifndef _WIN32_WINNT /* Minimum required for InitializeCriticalSectionAndSpinCount */ #define _WIN32_WINNT 0x0403 #endif #include #include #include #include #include #include "event2/util.h" #include "util-internal.h" #include "iocp-internal.h" #include "log-internal.h" #include "mm-internal.h" #include "event-internal.h" #include "evthread-internal.h" #define NOTIFICATION_KEY ((ULONG_PTR)-1) void event_overlapped_init_(struct event_overlapped *o, iocp_callback cb) { memset(o, 0, sizeof(struct event_overlapped)); o->cb = cb; } static void handle_entry(OVERLAPPED *o, ULONG_PTR completion_key, DWORD nBytes, int ok) { struct event_overlapped *eo = EVUTIL_UPCAST(o, struct event_overlapped, overlapped); eo->cb(eo, completion_key, nBytes, ok); } static void loop(void *port_) { struct event_iocp_port *port = port_; long ms = port->ms; HANDLE p = port->port; if (ms <= 0) ms = INFINITE; while (1) { OVERLAPPED *overlapped=NULL; ULONG_PTR key=0; DWORD bytes=0; int ok = GetQueuedCompletionStatus(p, &bytes, &key, &overlapped, ms); EnterCriticalSection(&port->lock); if (port->shutdown) { if (--port->n_live_threads == 0) ReleaseSemaphore(port->shutdownSemaphore, 1, NULL); LeaveCriticalSection(&port->lock); return; } LeaveCriticalSection(&port->lock); if (key != NOTIFICATION_KEY && overlapped) handle_entry(overlapped, key, bytes, ok); else if (!overlapped) break; } event_warnx("GetQueuedCompletionStatus exited with no event."); EnterCriticalSection(&port->lock); if (--port->n_live_threads == 0) ReleaseSemaphore(port->shutdownSemaphore, 1, NULL); LeaveCriticalSection(&port->lock); } int event_iocp_port_associate_(struct event_iocp_port *port, evutil_socket_t fd, ev_uintptr_t key) { HANDLE h; h = CreateIoCompletionPort((HANDLE)fd, port->port, key, port->n_threads); if (!h) return -1; return 0; } static void * get_extension_function(SOCKET s, const GUID *which_fn) { void *ptr = NULL; DWORD bytes=0; WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, (GUID*)which_fn, sizeof(*which_fn), &ptr, sizeof(ptr), &bytes, NULL, NULL); /* No need to detect errors here: if ptr is set, then we have a good function pointer. Otherwise, we should behave as if we had no function pointer. */ return ptr; } /* Mingw doesn't have these in its mswsock.h. The values are copied from wine.h. Perhaps if we copy them exactly, the cargo will come again. */ #ifndef WSAID_ACCEPTEX #define WSAID_ACCEPTEX \ {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} #endif #ifndef WSAID_CONNECTEX #define WSAID_CONNECTEX \ {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} #endif #ifndef WSAID_GETACCEPTEXSOCKADDRS #define WSAID_GETACCEPTEXSOCKADDRS \ {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} #endif static int extension_fns_initialized = 0; static void init_extension_functions(struct win32_extension_fns *ext) { const GUID acceptex = WSAID_ACCEPTEX; const GUID connectex = WSAID_CONNECTEX; const GUID getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; SOCKET s = socket(AF_INET, SOCK_STREAM, 0); if (s == EVUTIL_INVALID_SOCKET) return; ext->AcceptEx = get_extension_function(s, &acceptex); ext->ConnectEx = get_extension_function(s, &connectex); ext->GetAcceptExSockaddrs = get_extension_function(s, &getacceptexsockaddrs); closesocket(s); extension_fns_initialized = 1; } static struct win32_extension_fns the_extension_fns; const struct win32_extension_fns * event_get_win32_extension_fns_(void) { return &the_extension_fns; } #define N_CPUS_DEFAULT 2 struct event_iocp_port * event_iocp_port_launch_(int n_cpus) { struct event_iocp_port *port; int i; if (!extension_fns_initialized) init_extension_functions(&the_extension_fns); if (!(port = mm_calloc(1, sizeof(struct event_iocp_port)))) return NULL; if (n_cpus <= 0) n_cpus = N_CPUS_DEFAULT; port->n_threads = n_cpus * 2; port->threads = mm_calloc(port->n_threads, sizeof(HANDLE)); if (!port->threads) goto err; port->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, n_cpus); port->ms = -1; if (!port->port) goto err; port->shutdownSemaphore = CreateSemaphore(NULL, 0, 1, NULL); if (!port->shutdownSemaphore) goto err; for (i=0; in_threads; ++i) { ev_uintptr_t th = _beginthread(loop, 0, port); if (th == (ev_uintptr_t)-1) goto err; port->threads[i] = (HANDLE)th; ++port->n_live_threads; } InitializeCriticalSectionAndSpinCount(&port->lock, 1000); return port; err: if (port->port) CloseHandle(port->port); if (port->threads) mm_free(port->threads); if (port->shutdownSemaphore) CloseHandle(port->shutdownSemaphore); mm_free(port); return NULL; } static void event_iocp_port_unlock_and_free_(struct event_iocp_port *port) { DeleteCriticalSection(&port->lock); CloseHandle(port->port); CloseHandle(port->shutdownSemaphore); mm_free(port->threads); mm_free(port); } static int event_iocp_notify_all(struct event_iocp_port *port) { int i, r, ok=1; for (i=0; in_threads; ++i) { r = PostQueuedCompletionStatus(port->port, 0, NOTIFICATION_KEY, NULL); if (!r) ok = 0; } return ok ? 0 : -1; } int event_iocp_shutdown_(struct event_iocp_port *port, long waitMsec) { DWORD ms = INFINITE; int n; EnterCriticalSection(&port->lock); port->shutdown = 1; LeaveCriticalSection(&port->lock); event_iocp_notify_all(port); if (waitMsec >= 0) ms = waitMsec; WaitForSingleObject(port->shutdownSemaphore, ms); EnterCriticalSection(&port->lock); n = port->n_live_threads; LeaveCriticalSection(&port->lock); if (n == 0) { event_iocp_port_unlock_and_free_(port); return 0; } else { return -1; } } int event_iocp_activate_overlapped_( struct event_iocp_port *port, struct event_overlapped *o, ev_uintptr_t key, ev_uint32_t n) { BOOL r; r = PostQueuedCompletionStatus(port->port, n, key, &o->overlapped); return (r==0) ? -1 : 0; } struct event_iocp_port * event_base_get_iocp_(struct event_base *base) { #ifdef _WIN32 return base->iocp; #else return NULL; #endif } libevent-2.1.13-stable/strlcpy-internal.h0000644000175000017500000000064215215775075017405 0ustar00nickmnickm#ifndef STRLCPY_INTERNAL_H_INCLUDED_ #define STRLCPY_INTERNAL_H_INCLUDED_ #ifdef __cplusplus extern "C" { #endif #include "event2/event-config.h" #include "event2/visibility.h" #include "evconfig-private.h" #ifndef EVENT__HAVE_STRLCPY #include EVENT2_EXPORT_SYMBOL size_t event_strlcpy_(char *dst, const char *src, size_t siz); #define strlcpy event_strlcpy_ #endif #ifdef __cplusplus } #endif #endif libevent-2.1.13-stable/changelist-internal.h0000644000175000017500000001102315215775075020021 0ustar00nickmnickm/* * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CHANGELIST_INTERNAL_H_INCLUDED_ #define CHANGELIST_INTERNAL_H_INCLUDED_ /* A "changelist" is a list of all the fd status changes that should be made between calls to the backend's dispatch function. There are a few reasons that a backend would want to queue changes like this rather than processing them immediately. 1) Sometimes applications will add and delete the same event more than once between calls to dispatch. Processing these changes immediately is needless, and potentially expensive (especially if we're on a system that makes one syscall per changed event). 2) Sometimes we can coalesce multiple changes on the same fd into a single syscall if we know about them in advance. For example, epoll can do an add and a delete at the same time, but only if we have found out about both of them before we tell epoll. 3) Sometimes adding an event that we immediately delete can cause unintended consequences: in kqueue, this makes pending events get reported spuriously. */ #include "event2/util.h" /** Represents a */ struct event_change { /** The fd or signal whose events are to be changed */ evutil_socket_t fd; /* The events that were enabled on the fd before any of these changes were made. May include EV_READ or EV_WRITE. */ short old_events; /* The changes that we want to make in reading and writing on this fd. * If this is a signal, then read_change has EV_CHANGE_SIGNAL set, * and write_change is unused. */ ev_uint8_t read_change; ev_uint8_t write_change; ev_uint8_t close_change; }; /* Flags for read_change and write_change. */ /* If set, add the event. */ #define EV_CHANGE_ADD 0x01 /* If set, delete the event. Exclusive with EV_CHANGE_ADD */ #define EV_CHANGE_DEL 0x02 /* If set, this event refers a signal, not an fd. */ #define EV_CHANGE_SIGNAL EV_SIGNAL /* Set for persistent events. Currently not used. */ #define EV_CHANGE_PERSIST EV_PERSIST /* Set for adding edge-triggered events. */ #define EV_CHANGE_ET EV_ET /* The value of fdinfo_size that a backend should use if it is letting * changelist handle its add and delete functions. */ #define EVENT_CHANGELIST_FDINFO_SIZE sizeof(int) /** Set up the data fields in a changelist. */ void event_changelist_init_(struct event_changelist *changelist); /** Remove every change in the changelist, and make corresponding changes * in the event maps in the base. This function is generally used right * after making all the changes in the changelist. */ void event_changelist_remove_all_(struct event_changelist *changelist, struct event_base *base); /** Free all memory held in a changelist. */ void event_changelist_freemem_(struct event_changelist *changelist); /** Implementation of eventop_add that queues the event in a changelist. */ int event_changelist_add_(struct event_base *base, evutil_socket_t fd, short old, short events, void *p); /** Implementation of eventop_del that queues the event in a changelist. */ int event_changelist_del_(struct event_base *base, evutil_socket_t fd, short old, short events, void *p); #endif libevent-2.1.13-stable/log.c0000644000175000017500000001253115215775075014647 0ustar00nickmnickm/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ /* * log.c * * Based on err.c, which was adapted from OpenBSD libc *err* *warn* code. * * Copyright (c) 2005-2012 Niels Provos and Nick Mathewson * * Copyright (c) 2000 Dug Song * * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "event2/event-config.h" #include "evconfig-private.h" #ifdef _WIN32 #include #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include "event2/event.h" #include "event2/util.h" #include "log-internal.h" static void event_log(int severity, const char *msg); static void event_exit(int errcode) EV_NORETURN; static event_fatal_cb fatal_fn = NULL; #ifdef EVENT_DEBUG_LOGGING_ENABLED #ifdef USE_DEBUG #define DEFAULT_MASK EVENT_DBG_ALL #else #define DEFAULT_MASK 0 #endif EVENT2_EXPORT_SYMBOL ev_uint32_t event_debug_logging_mask_ = DEFAULT_MASK; #endif /* EVENT_DEBUG_LOGGING_ENABLED */ void event_enable_debug_logging(ev_uint32_t which) { #ifdef EVENT_DEBUG_LOGGING_ENABLED event_debug_logging_mask_ = which; #endif } void event_set_fatal_callback(event_fatal_cb cb) { fatal_fn = cb; } static void event_exit(int errcode) { if (fatal_fn) { fatal_fn(errcode); exit(errcode); /* should never be reached */ } else if (errcode == EVENT_ERR_ABORT_) abort(); else exit(errcode); } void event_err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); event_logv_(EVENT_LOG_ERR, strerror(errno), fmt, ap); va_end(ap); event_exit(eval); } void event_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); event_logv_(EVENT_LOG_WARN, strerror(errno), fmt, ap); va_end(ap); } void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) { va_list ap; int err = evutil_socket_geterror(sock); va_start(ap, fmt); event_logv_(EVENT_LOG_ERR, evutil_socket_error_to_string(err), fmt, ap); va_end(ap); event_exit(eval); } void event_sock_warn(evutil_socket_t sock, const char *fmt, ...) { va_list ap; int err = evutil_socket_geterror(sock); va_start(ap, fmt); event_logv_(EVENT_LOG_WARN, evutil_socket_error_to_string(err), fmt, ap); va_end(ap); } void event_errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); event_logv_(EVENT_LOG_ERR, NULL, fmt, ap); va_end(ap); event_exit(eval); } void event_warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); event_logv_(EVENT_LOG_WARN, NULL, fmt, ap); va_end(ap); } void event_msgx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); event_logv_(EVENT_LOG_MSG, NULL, fmt, ap); va_end(ap); } void event_debugx_(const char *fmt, ...) { va_list ap; va_start(ap, fmt); event_logv_(EVENT_LOG_DEBUG, NULL, fmt, ap); va_end(ap); } void event_logv_(int severity, const char *errstr, const char *fmt, va_list ap) { char buf[1024]; size_t len; if (severity == EVENT_LOG_DEBUG && !event_debug_get_logging_mask_()) return; if (fmt != NULL) evutil_vsnprintf(buf, sizeof(buf), fmt, ap); else buf[0] = '\0'; if (errstr) { len = strlen(buf); if (len < sizeof(buf) - 3) { evutil_snprintf(buf + len, sizeof(buf) - len, ": %s", errstr); } } event_log(severity, buf); } static event_log_cb log_fn = NULL; void event_set_log_callback(event_log_cb cb) { log_fn = cb; } static void event_log(int severity, const char *msg) { if (log_fn) log_fn(severity, msg); else { const char *severity_str; switch (severity) { case EVENT_LOG_DEBUG: severity_str = "debug"; break; case EVENT_LOG_MSG: severity_str = "msg"; break; case EVENT_LOG_WARN: severity_str = "warn"; break; case EVENT_LOG_ERR: severity_str = "err"; break; default: severity_str = "???"; break; } (void)fprintf(stderr, "[%s] %s\n", severity_str, msg); } } libevent-2.1.13-stable/event_rpcgen.py0000755000175000017500000015323215215775075016762 0ustar00nickmnickm#!/usr/bin/env python # # Copyright (c) 2005-2007 Niels Provos # Copyright (c) 2007-2012 Niels Provos and Nick Mathewson # All rights reserved. # # Generates marshaling code based on libevent. # pylint: disable=too-many-lines # pylint: disable=too-many-branches # pylint: disable=too-many-public-methods # pylint: disable=too-many-statements # pylint: disable=global-statement # TODO: # 1) propagate the arguments/options parsed by argparse down to the # instantiated factory objects. # 2) move the globals into a class that manages execution, including the # progress outputs that go to stderr at the moment. # 3) emit other languages. import argparse import re import sys _NAME = "event_rpcgen.py" _VERSION = "0.1" # Globals LINE_COUNT = 0 CPPCOMMENT_RE = re.compile(r"\/\/.*$") NONIDENT_RE = re.compile(r"\W") PREPROCESSOR_DEF_RE = re.compile(r"^#define") STRUCT_REF_RE = re.compile(r"^struct\[(?P[a-zA-Z_][a-zA-Z0-9_]*)\]$") STRUCT_DEF_RE = re.compile(r"^struct +[a-zA-Z_][a-zA-Z0-9_]* *{$") WHITESPACE_RE = re.compile(r"\s+") HEADER_DIRECT = [] CPP_DIRECT = [] QUIETLY = False def declare(s): if not QUIETLY: print(s) def TranslateList(mylist, mydict): return [x % mydict for x in mylist] class RpcGenError(Exception): """An Exception class for parse errors.""" def __init__(self, why): # pylint: disable=super-init-not-called self.why = why def __str__(self): return str(self.why) # Holds everything that makes a struct class Struct(object): def __init__(self, name): self._name = name self._entries = [] self._tags = {} declare(" Created struct: %s" % name) def AddEntry(self, entry): if entry.Tag() in self._tags: raise RpcGenError( 'Entry "%s" duplicates tag number %d from "%s" ' "around line %d" % (entry.Name(), entry.Tag(), self._tags[entry.Tag()], LINE_COUNT) ) self._entries.append(entry) self._tags[entry.Tag()] = entry.Name() declare(" Added entry: %s" % entry.Name()) def Name(self): return self._name def EntryTagName(self, entry): """Creates the name inside an enumeration for distinguishing data types.""" name = "%s_%s" % (self._name, entry.Name()) return name.upper() @staticmethod def PrintIndented(filep, ident, code): """Takes an array, add indentation to each entry and prints it.""" for entry in code: filep.write("%s%s\n" % (ident, entry)) class StructCCode(Struct): """ Knows how to generate C code for a struct """ def __init__(self, name): Struct.__init__(self, name) def PrintTags(self, filep): """Prints the tag definitions for a structure.""" filep.write("/* Tag definition for %s */\n" % self._name) filep.write("enum %s_ {\n" % self._name.lower()) for entry in self._entries: filep.write(" %s=%d,\n" % (self.EntryTagName(entry), entry.Tag())) filep.write(" %s_MAX_TAGS\n" % (self._name.upper())) filep.write("};\n\n") def PrintForwardDeclaration(self, filep): filep.write("struct %s;\n" % self._name) def PrintDeclaration(self, filep): filep.write("/* Structure declaration for %s */\n" % self._name) filep.write("struct %s_access_ {\n" % self._name) for entry in self._entries: dcl = entry.AssignDeclaration("(*%s_assign)" % entry.Name()) dcl.extend(entry.GetDeclaration("(*%s_get)" % entry.Name())) if entry.Array(): dcl.extend(entry.AddDeclaration("(*%s_add)" % entry.Name())) self.PrintIndented(filep, " ", dcl) filep.write("};\n\n") filep.write("struct %s {\n" % self._name) filep.write(" struct %s_access_ *base;\n\n" % self._name) for entry in self._entries: dcl = entry.Declaration() self.PrintIndented(filep, " ", dcl) filep.write("\n") for entry in self._entries: filep.write(" ev_uint8_t %s_set;\n" % entry.Name()) filep.write("};\n\n") filep.write( """struct %(name)s *%(name)s_new(void); struct %(name)s *%(name)s_new_with_arg(void *); void %(name)s_free(struct %(name)s *); void %(name)s_clear(struct %(name)s *); void %(name)s_marshal(struct evbuffer *, const struct %(name)s *); int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *); int %(name)s_complete(struct %(name)s *); void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t, const struct %(name)s *); int evtag_unmarshal_%(name)s(struct evbuffer *, ev_uint32_t, struct %(name)s *);\n""" % {"name": self._name} ) # Write a setting function of every variable for entry in self._entries: self.PrintIndented( filep, "", entry.AssignDeclaration(entry.AssignFuncName()) ) self.PrintIndented(filep, "", entry.GetDeclaration(entry.GetFuncName())) if entry.Array(): self.PrintIndented(filep, "", entry.AddDeclaration(entry.AddFuncName())) filep.write("/* --- %s done --- */\n\n" % self._name) def PrintCode(self, filep): filep.write( """/* * Implementation of %s */ """ % (self._name) ) filep.write( """ static struct %(name)s_access_ %(name)s_base__ = { """ % {"name": self._name} ) for entry in self._entries: self.PrintIndented(filep, " ", entry.CodeBase()) filep.write("};\n\n") # Creation filep.write( """struct %(name)s * %(name)s_new(void) { return %(name)s_new_with_arg(NULL); } struct %(name)s * %(name)s_new_with_arg(void *unused) { struct %(name)s *tmp; if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) { event_warn("%%s: malloc", __func__); return (NULL); } tmp->base = &%(name)s_base__; """ % {"name": self._name} ) for entry in self._entries: self.PrintIndented(filep, " ", entry.CodeInitialize("tmp")) filep.write(" tmp->%s_set = 0;\n\n" % entry.Name()) filep.write( """ return (tmp); } """ ) # Adding for entry in self._entries: if entry.Array(): self.PrintIndented(filep, "", entry.CodeAdd()) filep.write("\n") # Assigning for entry in self._entries: self.PrintIndented(filep, "", entry.CodeAssign()) filep.write("\n") # Getting for entry in self._entries: self.PrintIndented(filep, "", entry.CodeGet()) filep.write("\n") # Clearing filep.write( """void %(name)s_clear(struct %(name)s *tmp) { """ % {"name": self._name} ) for entry in self._entries: self.PrintIndented(filep, " ", entry.CodeClear("tmp")) filep.write("}\n\n") # Freeing filep.write( """void %(name)s_free(struct %(name)s *tmp) { """ % {"name": self._name} ) for entry in self._entries: self.PrintIndented(filep, " ", entry.CodeFree("tmp")) filep.write( """ free(tmp); } """ ) # Marshaling filep.write( """void %(name)s_marshal(struct evbuffer *evbuf, const struct %(name)s *tmp) { """ % {"name": self._name} ) for entry in self._entries: indent = " " # Optional entries do not have to be set if entry.Optional(): indent += " " filep.write(" if (tmp->%s_set) {\n" % entry.Name()) self.PrintIndented( filep, indent, entry.CodeMarshal( "evbuf", self.EntryTagName(entry), entry.GetVarName("tmp"), entry.GetVarLen("tmp"), ), ) if entry.Optional(): filep.write(" }\n") filep.write("}\n\n") # Unmarshaling filep.write( """int %(name)s_unmarshal(struct %(name)s *tmp, struct evbuffer *evbuf) { ev_uint32_t tag; while (evbuffer_get_length(evbuf) > 0) { if (evtag_peek(evbuf, &tag) == -1) return (-1); switch (tag) { """ % {"name": self._name} ) for entry in self._entries: filep.write(" case %s:\n" % (self.EntryTagName(entry))) if not entry.Array(): filep.write( """ if (tmp->%s_set) return (-1); """ % (entry.Name()) ) self.PrintIndented( filep, " ", entry.CodeUnmarshal( "evbuf", self.EntryTagName(entry), entry.GetVarName("tmp"), entry.GetVarLen("tmp"), ), ) filep.write( """ tmp->%s_set = 1; break; """ % (entry.Name()) ) filep.write( """ default: return -1; } } """ ) # Check if it was decoded completely filep.write( """ if (%(name)s_complete(tmp) == -1) return (-1); return (0); } """ % {"name": self._name} ) # Checking if a structure has all the required data filep.write( """ int %(name)s_complete(struct %(name)s *msg) { """ % {"name": self._name} ) for entry in self._entries: if not entry.Optional(): code = [ """if (!msg->%(name)s_set) return (-1);""" ] code = TranslateList(code, entry.GetTranslation()) self.PrintIndented(filep, " ", code) self.PrintIndented( filep, " ", entry.CodeComplete("msg", entry.GetVarName("msg")) ) filep.write( """ return (0); } """ ) # Complete message unmarshaling filep.write( """ int evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t need_tag, struct %(name)s *msg) { ev_uint32_t tag; int res = -1; struct evbuffer *tmp = evbuffer_new(); if (evtag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag) goto error; if (%(name)s_unmarshal(msg, tmp) == -1) goto error; res = 0; error: evbuffer_free(tmp); return (res); } """ % {"name": self._name} ) # Complete message marshaling filep.write( """ void evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag, const struct %(name)s *msg) { struct evbuffer *buf_ = evbuffer_new(); assert(buf_ != NULL); %(name)s_marshal(buf_, msg); evtag_marshal_buffer(evbuf, tag, buf_); evbuffer_free(buf_); } """ % {"name": self._name} ) class Entry(object): def __init__(self, ent_type, name, tag): self._type = ent_type self._name = name self._tag = int(tag) self._ctype = ent_type self._optional = False self._can_be_array = False self._array = False self._line_count = -1 self._struct = None self._refname = None self._optpointer = True self._optaddarg = True @staticmethod def GetInitializer(): raise NotImplementedError("Entry does not provide an initializer") def SetStruct(self, struct): self._struct = struct def LineCount(self): assert self._line_count != -1 return self._line_count def SetLineCount(self, number): self._line_count = number def Array(self): return self._array def Optional(self): return self._optional def Tag(self): return self._tag def Name(self): return self._name def Type(self): return self._type def MakeArray(self): self._array = True def MakeOptional(self): self._optional = True def Verify(self): if self.Array() and not self._can_be_array: raise RpcGenError( 'Entry "%s" cannot be created as an array ' "around line %d" % (self._name, self.LineCount()) ) if not self._struct: raise RpcGenError( 'Entry "%s" does not know which struct it belongs to ' "around line %d" % (self._name, self.LineCount()) ) if self._optional and self._array: raise RpcGenError( 'Entry "%s" has illegal combination of optional and array ' "around line %d" % (self._name, self.LineCount()) ) def GetTranslation(self, extradict=None): if extradict is None: extradict = {} mapping = { "parent_name": self._struct.Name(), "name": self._name, "ctype": self._ctype, "refname": self._refname, "optpointer": self._optpointer and "*" or "", "optreference": self._optpointer and "&" or "", "optaddarg": self._optaddarg and ", const %s value" % self._ctype or "", } for (k, v) in list(extradict.items()): mapping[k] = v return mapping def GetVarName(self, var): return "%(var)s->%(name)s_data" % self.GetTranslation({"var": var}) def GetVarLen(self, _var): return "sizeof(%s)" % self._ctype def GetFuncName(self): return "%s_%s_get" % (self._struct.Name(), self._name) def GetDeclaration(self, funcname): code = [ "int %s(struct %s *, %s *);" % (funcname, self._struct.Name(), self._ctype) ] return code def CodeGet(self): code = """int %(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, %(ctype)s *value) { if (msg->%(name)s_set != 1) return (-1); *value = msg->%(name)s_data; return (0); }""" code = code % self.GetTranslation() return code.split("\n") def AssignFuncName(self): return "%s_%s_assign" % (self._struct.Name(), self._name) def AddFuncName(self): return "%s_%s_add" % (self._struct.Name(), self._name) def AssignDeclaration(self, funcname): code = [ "int %s(struct %s *, const %s);" % (funcname, self._struct.Name(), self._ctype) ] return code def CodeAssign(self): code = [ "int", "%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg," " const %(ctype)s value)", "{", " msg->%(name)s_set = 1;", " msg->%(name)s_data = value;", " return (0);", "}", ] code = "\n".join(code) code = code % self.GetTranslation() return code.split("\n") def CodeClear(self, structname): code = ["%s->%s_set = 0;" % (structname, self.Name())] return code @staticmethod def CodeComplete(_structname, _var_name): return [] @staticmethod def CodeFree(_name): return [] def CodeBase(self): code = ["%(parent_name)s_%(name)s_assign,", "%(parent_name)s_%(name)s_get,"] if self.Array(): code.append("%(parent_name)s_%(name)s_add,") code = "\n".join(code) code = code % self.GetTranslation() return code.split("\n") class EntryBytes(Entry): def __init__(self, ent_type, name, tag, length): # Init base class super(EntryBytes, self).__init__(ent_type, name, tag) self._length = length self._ctype = "ev_uint8_t" @staticmethod def GetInitializer(): return "NULL" def GetVarLen(self, _var): return "(%s)" % self._length @staticmethod def CodeArrayAdd(varname, _value): # XXX: copy here return ["%(varname)s = NULL;" % {"varname": varname}] def GetDeclaration(self, funcname): code = [ "int %s(struct %s *, %s **);" % (funcname, self._struct.Name(), self._ctype) ] return code def AssignDeclaration(self, funcname): code = [ "int %s(struct %s *, const %s *);" % (funcname, self._struct.Name(), self._ctype) ] return code def Declaration(self): dcl = ["ev_uint8_t %s_data[%s];" % (self._name, self._length)] return dcl def CodeGet(self): name = self._name code = [ "int", "%s_%s_get(struct %s *msg, %s **value)" % (self._struct.Name(), name, self._struct.Name(), self._ctype), "{", " if (msg->%s_set != 1)" % name, " return (-1);", " *value = msg->%s_data;" % name, " return (0);", "}", ] return code def CodeAssign(self): name = self._name code = [ "int", "%s_%s_assign(struct %s *msg, const %s *value)" % (self._struct.Name(), name, self._struct.Name(), self._ctype), "{", " msg->%s_set = 1;" % name, " memcpy(msg->%s_data, value, %s);" % (name, self._length), " return (0);", "}", ] return code def CodeUnmarshal(self, buf, tag_name, var_name, var_len): code = [ "if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, " "%(var)s, %(varlen)s) == -1) {", ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', " return (-1);", "}", ] return TranslateList( code, self.GetTranslation( {"var": var_name, "varlen": var_len, "buf": buf, "tag": tag_name} ), ) @staticmethod def CodeMarshal(buf, tag_name, var_name, var_len): code = ["evtag_marshal(%s, %s, %s, %s);" % (buf, tag_name, var_name, var_len)] return code def CodeClear(self, structname): code = [ "%s->%s_set = 0;" % (structname, self.Name()), "memset(%s->%s_data, 0, sizeof(%s->%s_data));" % (structname, self._name, structname, self._name), ] return code def CodeInitialize(self, name): code = [ "memset(%s->%s_data, 0, sizeof(%s->%s_data));" % (name, self._name, name, self._name) ] return code def Verify(self): if not self._length: raise RpcGenError( 'Entry "%s" needs a length ' "around line %d" % (self._name, self.LineCount()) ) super(EntryBytes, self).Verify() class EntryInt(Entry): def __init__(self, ent_type, name, tag, bits=32): # Init base class super(EntryInt, self).__init__(ent_type, name, tag) self._can_be_array = True if bits == 32: self._ctype = "ev_uint32_t" self._marshal_type = "int" if bits == 64: self._ctype = "ev_uint64_t" self._marshal_type = "int64" @staticmethod def GetInitializer(): return "0" @staticmethod def CodeArrayFree(_var): return [] @staticmethod def CodeArrayAssign(varname, srcvar): return ["%(varname)s = %(srcvar)s;" % {"varname": varname, "srcvar": srcvar}] @staticmethod def CodeArrayAdd(varname, value): """Returns a new entry of this type.""" return ["%(varname)s = %(value)s;" % {"varname": varname, "value": value}] def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): code = [ "if (evtag_unmarshal_%(ma)s(%(buf)s, %(tag)s, &%(var)s) == -1) {", ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', " return (-1);", "}", ] code = "\n".join(code) % self.GetTranslation( {"ma": self._marshal_type, "buf": buf, "tag": tag_name, "var": var_name} ) return code.split("\n") def CodeMarshal(self, buf, tag_name, var_name, _var_len): code = [ "evtag_marshal_%s(%s, %s, %s);" % (self._marshal_type, buf, tag_name, var_name) ] return code def Declaration(self): dcl = ["%s %s_data;" % (self._ctype, self._name)] return dcl def CodeInitialize(self, name): code = ["%s->%s_data = 0;" % (name, self._name)] return code class EntryString(Entry): def __init__(self, ent_type, name, tag): # Init base class super(EntryString, self).__init__(ent_type, name, tag) self._can_be_array = True self._ctype = "char *" @staticmethod def GetInitializer(): return "NULL" @staticmethod def CodeArrayFree(varname): code = ["if (%(var)s != NULL) free(%(var)s);"] return TranslateList(code, {"var": varname}) @staticmethod def CodeArrayAssign(varname, srcvar): code = [ "if (%(var)s != NULL)", " free(%(var)s);", "%(var)s = strdup(%(srcvar)s);", "if (%(var)s == NULL) {", ' event_warnx("%%s: strdup", __func__);', " return (-1);", "}", ] return TranslateList(code, {"var": varname, "srcvar": srcvar}) @staticmethod def CodeArrayAdd(varname, value): code = [ "if (%(value)s != NULL) {", " %(var)s = strdup(%(value)s);", " if (%(var)s == NULL) {", " goto error;", " }", "} else {", " %(var)s = NULL;", "}", ] return TranslateList(code, {"var": varname, "value": value}) def GetVarLen(self, var): return "strlen(%s)" % self.GetVarName(var) @staticmethod def CodeMakeInitalize(varname): return "%(varname)s = NULL;" % {"varname": varname} def CodeAssign(self): code = """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, const %(ctype)s value) { if (msg->%(name)s_data != NULL) free(msg->%(name)s_data); if ((msg->%(name)s_data = strdup(value)) == NULL) return (-1); msg->%(name)s_set = 1; return (0); }""" % ( self.GetTranslation() ) return code.split("\n") def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): code = [ "if (evtag_unmarshal_string(%(buf)s, %(tag)s, &%(var)s) == -1) {", ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', " return (-1);", "}", ] code = "\n".join(code) % self.GetTranslation( {"buf": buf, "tag": tag_name, "var": var_name} ) return code.split("\n") @staticmethod def CodeMarshal(buf, tag_name, var_name, _var_len): code = ["evtag_marshal_string(%s, %s, %s);" % (buf, tag_name, var_name)] return code def CodeClear(self, structname): code = [ "if (%s->%s_set == 1) {" % (structname, self.Name()), " free(%s->%s_data);" % (structname, self.Name()), " %s->%s_data = NULL;" % (structname, self.Name()), " %s->%s_set = 0;" % (structname, self.Name()), "}", ] return code def CodeInitialize(self, name): code = ["%s->%s_data = NULL;" % (name, self._name)] return code def CodeFree(self, name): code = [ "if (%s->%s_data != NULL)" % (name, self._name), " free (%s->%s_data);" % (name, self._name), ] return code def Declaration(self): dcl = ["char *%s_data;" % self._name] return dcl class EntryStruct(Entry): def __init__(self, ent_type, name, tag, refname): # Init base class super(EntryStruct, self).__init__(ent_type, name, tag) self._optpointer = False self._can_be_array = True self._refname = refname self._ctype = "struct %s*" % refname self._optaddarg = False def GetInitializer(self): return "NULL" def GetVarLen(self, _var): return "-1" def CodeArrayAdd(self, varname, _value): code = [ "%(varname)s = %(refname)s_new();", "if (%(varname)s == NULL)", " goto error;", ] return TranslateList(code, self.GetTranslation({"varname": varname})) def CodeArrayFree(self, var): code = ["%(refname)s_free(%(var)s);" % self.GetTranslation({"var": var})] return code def CodeArrayAssign(self, var, srcvar): code = [ "int had_error = 0;", "struct evbuffer *tmp = NULL;", "%(refname)s_clear(%(var)s);", "if ((tmp = evbuffer_new()) == NULL) {", ' event_warn("%%s: evbuffer_new()", __func__);', " had_error = 1;", " goto done;", "}", "%(refname)s_marshal(tmp, %(srcvar)s);", "if (%(refname)s_unmarshal(%(var)s, tmp) == -1) {", ' event_warnx("%%s: %(refname)s_unmarshal", __func__);', " had_error = 1;", " goto done;", "}", "done:", "if (tmp != NULL)", " evbuffer_free(tmp);", "if (had_error) {", " %(refname)s_clear(%(var)s);", " return (-1);", "}", ] return TranslateList(code, self.GetTranslation({"var": var, "srcvar": srcvar})) def CodeGet(self): name = self._name code = [ "int", "%s_%s_get(struct %s *msg, %s *value)" % (self._struct.Name(), name, self._struct.Name(), self._ctype), "{", " if (msg->%s_set != 1) {" % name, " msg->%s_data = %s_new();" % (name, self._refname), " if (msg->%s_data == NULL)" % name, " return (-1);", " msg->%s_set = 1;" % name, " }", " *value = msg->%s_data;" % name, " return (0);", "}", ] return code def CodeAssign(self): code = ( """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, const %(ctype)s value) { struct evbuffer *tmp = NULL; if (msg->%(name)s_set) { %(refname)s_clear(msg->%(name)s_data); msg->%(name)s_set = 0; } else { msg->%(name)s_data = %(refname)s_new(); if (msg->%(name)s_data == NULL) { event_warn("%%s: %(refname)s_new()", __func__); goto error; } } if ((tmp = evbuffer_new()) == NULL) { event_warn("%%s: evbuffer_new()", __func__); goto error; } %(refname)s_marshal(tmp, value); if (%(refname)s_unmarshal(msg->%(name)s_data, tmp) == -1) { event_warnx("%%s: %(refname)s_unmarshal", __func__); goto error; } msg->%(name)s_set = 1; evbuffer_free(tmp); return (0); error: if (tmp != NULL) evbuffer_free(tmp); if (msg->%(name)s_data != NULL) { %(refname)s_free(msg->%(name)s_data); msg->%(name)s_data = NULL; } return (-1); }""" % self.GetTranslation() ) return code.split("\n") def CodeComplete(self, structname, var_name): code = [ "if (%(structname)s->%(name)s_set && " "%(refname)s_complete(%(var)s) == -1)", " return (-1);", ] return TranslateList( code, self.GetTranslation({"structname": structname, "var": var_name}) ) def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): code = [ "%(var)s = %(refname)s_new();", "if (%(var)s == NULL)", " return (-1);", "if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag)s, ", " %(var)s) == -1) {", ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', " return (-1);", "}", ] code = "\n".join(code) % self.GetTranslation( {"buf": buf, "tag": tag_name, "var": var_name} ) return code.split("\n") def CodeMarshal(self, buf, tag_name, var_name, _var_len): code = [ "evtag_marshal_%s(%s, %s, %s);" % (self._refname, buf, tag_name, var_name) ] return code def CodeClear(self, structname): code = [ "if (%s->%s_set == 1) {" % (structname, self.Name()), " %s_free(%s->%s_data);" % (self._refname, structname, self.Name()), " %s->%s_data = NULL;" % (structname, self.Name()), " %s->%s_set = 0;" % (structname, self.Name()), "}", ] return code def CodeInitialize(self, name): code = ["%s->%s_data = NULL;" % (name, self._name)] return code def CodeFree(self, name): code = [ "if (%s->%s_data != NULL)" % (name, self._name), " %s_free(%s->%s_data);" % (self._refname, name, self._name), ] return code def Declaration(self): dcl = ["%s %s_data;" % (self._ctype, self._name)] return dcl class EntryVarBytes(Entry): def __init__(self, ent_type, name, tag): # Init base class super(EntryVarBytes, self).__init__(ent_type, name, tag) self._ctype = "ev_uint8_t *" @staticmethod def GetInitializer(): return "NULL" def GetVarLen(self, var): return "%(var)s->%(name)s_length" % self.GetTranslation({"var": var}) @staticmethod def CodeArrayAdd(varname, _value): # xxx: copy return ["%(varname)s = NULL;" % {"varname": varname}] def GetDeclaration(self, funcname): code = [ "int %s(struct %s *, %s *, ev_uint32_t *);" % (funcname, self._struct.Name(), self._ctype) ] return code def AssignDeclaration(self, funcname): code = [ "int %s(struct %s *, const %s, ev_uint32_t);" % (funcname, self._struct.Name(), self._ctype) ] return code def CodeAssign(self): name = self._name code = [ "int", "%s_%s_assign(struct %s *msg, " "const %s value, ev_uint32_t len)" % (self._struct.Name(), name, self._struct.Name(), self._ctype), "{", " if (msg->%s_data != NULL)" % name, " free (msg->%s_data);" % name, " msg->%s_data = malloc(len);" % name, " if (msg->%s_data == NULL)" % name, " return (-1);", " msg->%s_set = 1;" % name, " msg->%s_length = len;" % name, " memcpy(msg->%s_data, value, len);" % name, " return (0);", "}", ] return code def CodeGet(self): name = self._name code = [ "int", "%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)" % (self._struct.Name(), name, self._struct.Name(), self._ctype), "{", " if (msg->%s_set != 1)" % name, " return (-1);", " *value = msg->%s_data;" % name, " *plen = msg->%s_length;" % name, " return (0);", "}", ] return code def CodeUnmarshal(self, buf, tag_name, var_name, var_len): code = [ "if (evtag_payload_length(%(buf)s, &%(varlen)s) == -1)", " return (-1);", # We do not want DoS opportunities "if (%(varlen)s > evbuffer_get_length(%(buf)s))", " return (-1);", "if ((%(var)s = malloc(%(varlen)s)) == NULL)", " return (-1);", "if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, %(var)s, " "%(varlen)s) == -1) {", ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', " return (-1);", "}", ] code = "\n".join(code) % self.GetTranslation( {"buf": buf, "tag": tag_name, "var": var_name, "varlen": var_len} ) return code.split("\n") @staticmethod def CodeMarshal(buf, tag_name, var_name, var_len): code = ["evtag_marshal(%s, %s, %s, %s);" % (buf, tag_name, var_name, var_len)] return code def CodeClear(self, structname): code = [ "if (%s->%s_set == 1) {" % (structname, self.Name()), " free (%s->%s_data);" % (structname, self.Name()), " %s->%s_data = NULL;" % (structname, self.Name()), " %s->%s_length = 0;" % (structname, self.Name()), " %s->%s_set = 0;" % (structname, self.Name()), "}", ] return code def CodeInitialize(self, name): code = [ "%s->%s_data = NULL;" % (name, self._name), "%s->%s_length = 0;" % (name, self._name), ] return code def CodeFree(self, name): code = [ "if (%s->%s_data != NULL)" % (name, self._name), " free(%s->%s_data);" % (name, self._name), ] return code def Declaration(self): dcl = [ "ev_uint8_t *%s_data;" % self._name, "ev_uint32_t %s_length;" % self._name, ] return dcl class EntryArray(Entry): _index = None def __init__(self, entry): # Init base class super(EntryArray, self).__init__(entry._type, entry._name, entry._tag) self._entry = entry self._refname = entry._refname self._ctype = self._entry._ctype self._optional = True self._optpointer = self._entry._optpointer self._optaddarg = self._entry._optaddarg # provide a new function for accessing the variable name def GetVarName(var_name): return "%(var)s->%(name)s_data[%(index)s]" % self._entry.GetTranslation( {"var": var_name, "index": self._index} ) self._entry.GetVarName = GetVarName def GetInitializer(self): return "NULL" def GetVarName(self, var): return var def GetVarLen(self, _var_name): return "-1" def GetDeclaration(self, funcname): """Allows direct access to elements of the array.""" code = [ "int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);" % self.GetTranslation({"funcname": funcname}) ] return code def AssignDeclaration(self, funcname): code = [ "int %s(struct %s *, int, const %s);" % (funcname, self._struct.Name(), self._ctype) ] return code def AddDeclaration(self, funcname): code = [ "%(ctype)s %(optpointer)s " "%(funcname)s(struct %(parent_name)s *msg%(optaddarg)s);" % self.GetTranslation({"funcname": funcname}) ] return code def CodeGet(self): code = """int %(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, int offset, %(ctype)s *value) { if (!msg->%(name)s_set || offset < 0 || offset >= msg->%(name)s_length) return (-1); *value = msg->%(name)s_data[offset]; return (0); } """ % ( self.GetTranslation() ) return code.splitlines() def CodeAssign(self): code = [ "int", "%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off,", " const %(ctype)s value)", "{", " if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length)", " return (-1);", "", " {", ] code = TranslateList(code, self.GetTranslation()) codearrayassign = self._entry.CodeArrayAssign( "msg->%(name)s_data[off]" % self.GetTranslation(), "value" ) code += [" " + x for x in codearrayassign] code += TranslateList([" }", " return (0);", "}"], self.GetTranslation()) return code def CodeAdd(self): codearrayadd = self._entry.CodeArrayAdd( "msg->%(name)s_data[msg->%(name)s_length - 1]" % self.GetTranslation(), "value", ) code = [ "static int", "%(parent_name)s_%(name)s_expand_to_hold_more(" "struct %(parent_name)s *msg)", "{", " int tobe_allocated = msg->%(name)s_num_allocated;", " %(ctype)s* new_data = NULL;", " tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1;", " new_data = (%(ctype)s*) realloc(msg->%(name)s_data,", " tobe_allocated * sizeof(%(ctype)s));", " if (new_data == NULL)", " return -1;", " msg->%(name)s_data = new_data;", " msg->%(name)s_num_allocated = tobe_allocated;", " return 0;", "}", "", "%(ctype)s %(optpointer)s", "%(parent_name)s_%(name)s_add(struct %(parent_name)s *msg%(optaddarg)s)", "{", " if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) {", " if (%(parent_name)s_%(name)s_expand_to_hold_more(msg)<0)", " goto error;", " }", ] code = TranslateList(code, self.GetTranslation()) code += [" " + x for x in codearrayadd] code += TranslateList( [ " msg->%(name)s_set = 1;", " return %(optreference)s(msg->%(name)s_data[" "msg->%(name)s_length - 1]);", "error:", " --msg->%(name)s_length;", " return (NULL);", "}", ], self.GetTranslation(), ) return code def CodeComplete(self, structname, var_name): self._index = "i" tmp = self._entry.CodeComplete(structname, self._entry.GetVarName(var_name)) # skip the whole loop if there is nothing to check if not tmp: return [] translate = self.GetTranslation({"structname": structname}) code = [ "{", " int i;", " for (i = 0; i < %(structname)s->%(name)s_length; ++i) {", ] code = TranslateList(code, translate) code += [" " + x for x in tmp] code += [" }", "}"] return code def CodeUnmarshal(self, buf, tag_name, var_name, _var_len): translate = self.GetTranslation( { "var": var_name, "buf": buf, "tag": tag_name, "init": self._entry.GetInitializer(), } ) code = [ "if (%(var)s->%(name)s_length >= %(var)s->%(name)s_num_allocated &&", " %(parent_name)s_%(name)s_expand_to_hold_more(%(var)s) < 0) {", ' puts("HEY NOW");', " return (-1);", "}", ] # the unmarshal code directly returns code = TranslateList(code, translate) self._index = "%(var)s->%(name)s_length" % translate code += self._entry.CodeUnmarshal( buf, tag_name, self._entry.GetVarName(var_name), self._entry.GetVarLen(var_name), ) code += ["++%(var)s->%(name)s_length;" % translate] return code def CodeMarshal(self, buf, tag_name, var_name, _var_len): code = ["{", " int i;", " for (i = 0; i < %(var)s->%(name)s_length; ++i) {"] self._index = "i" code += self._entry.CodeMarshal( buf, tag_name, self._entry.GetVarName(var_name), self._entry.GetVarLen(var_name), ) code += [" }", "}"] code = "\n".join(code) % self.GetTranslation({"var": var_name}) return code.split("\n") def CodeClear(self, structname): translate = self.GetTranslation({"structname": structname}) codearrayfree = self._entry.CodeArrayFree( "%(structname)s->%(name)s_data[i]" % self.GetTranslation({"structname": structname}) ) code = ["if (%(structname)s->%(name)s_set == 1) {"] if codearrayfree: code += [ " int i;", " for (i = 0; i < %(structname)s->%(name)s_length; ++i) {", ] code = TranslateList(code, translate) if codearrayfree: code += [" " + x for x in codearrayfree] code += [" }"] code += TranslateList( [ " free(%(structname)s->%(name)s_data);", " %(structname)s->%(name)s_data = NULL;", " %(structname)s->%(name)s_set = 0;", " %(structname)s->%(name)s_length = 0;", " %(structname)s->%(name)s_num_allocated = 0;", "}", ], translate, ) return code def CodeInitialize(self, name): code = [ "%s->%s_data = NULL;" % (name, self._name), "%s->%s_length = 0;" % (name, self._name), "%s->%s_num_allocated = 0;" % (name, self._name), ] return code def CodeFree(self, structname): code = self.CodeClear(structname) code += TranslateList( ["free(%(structname)s->%(name)s_data);"], self.GetTranslation({"structname": structname}), ) return code def Declaration(self): dcl = [ "%s *%s_data;" % (self._ctype, self._name), "int %s_length;" % self._name, "int %s_num_allocated;" % self._name, ] return dcl def NormalizeLine(line): line = CPPCOMMENT_RE.sub("", line) line = line.strip() line = WHITESPACE_RE.sub(" ", line) return line ENTRY_NAME_RE = re.compile(r"(?P[^\[\]]+)(\[(?P.*)\])?") ENTRY_TAG_NUMBER_RE = re.compile(r"(0x)?\d+", re.I) def ProcessOneEntry(factory, newstruct, entry): optional = False array = False entry_type = "" name = "" tag = "" tag_set = None separator = "" fixed_length = "" for token in entry.split(" "): if not entry_type: if not optional and token == "optional": optional = True continue if not array and token == "array": array = True continue if not entry_type: entry_type = token continue if not name: res = ENTRY_NAME_RE.match(token) if not res: raise RpcGenError( r"""Cannot parse name: "%s" around line %d""" % (entry, LINE_COUNT) ) name = res.group("name") fixed_length = res.group("fixed_length") continue if not separator: separator = token if separator != "=": raise RpcGenError( r'''Expected "=" after name "%s" got "%s"''' % (name, token) ) continue if not tag_set: tag_set = 1 if not ENTRY_TAG_NUMBER_RE.match(token): raise RpcGenError(r'''Expected tag number: "%s"''' % (entry)) tag = int(token, 0) continue raise RpcGenError(r'''Cannot parse "%s"''' % (entry)) if not tag_set: raise RpcGenError(r'''Need tag number: "%s"''' % (entry)) # Create the right entry if entry_type == "bytes": if fixed_length: newentry = factory.EntryBytes(entry_type, name, tag, fixed_length) else: newentry = factory.EntryVarBytes(entry_type, name, tag) elif entry_type == "int" and not fixed_length: newentry = factory.EntryInt(entry_type, name, tag) elif entry_type == "int64" and not fixed_length: newentry = factory.EntryInt(entry_type, name, tag, bits=64) elif entry_type == "string" and not fixed_length: newentry = factory.EntryString(entry_type, name, tag) else: res = STRUCT_REF_RE.match(entry_type) if res: # References another struct defined in our file newentry = factory.EntryStruct(entry_type, name, tag, res.group("name")) else: raise RpcGenError('Bad type: "%s" in "%s"' % (entry_type, entry)) structs = [] if optional: newentry.MakeOptional() if array: newentry.MakeArray() newentry.SetStruct(newstruct) newentry.SetLineCount(LINE_COUNT) newentry.Verify() if array: # We need to encapsulate this entry into a struct newentry = factory.EntryArray(newentry) newentry.SetStruct(newstruct) newentry.SetLineCount(LINE_COUNT) newentry.MakeArray() newstruct.AddEntry(newentry) return structs def ProcessStruct(factory, data): tokens = data.split(" ") # First three tokens are: 'struct' 'name' '{' newstruct = factory.Struct(tokens[1]) inside = " ".join(tokens[3:-1]) tokens = inside.split(";") structs = [] for entry in tokens: entry = NormalizeLine(entry) if not entry: continue # It's possible that new structs get defined in here structs.extend(ProcessOneEntry(factory, newstruct, entry)) structs.append(newstruct) return structs C_COMMENT_START = "/*" C_COMMENT_END = "*/" C_COMMENT_START_RE = re.compile(re.escape(C_COMMENT_START)) C_COMMENT_END_RE = re.compile(re.escape(C_COMMENT_END)) C_COMMENT_START_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_START))) C_COMMENT_END_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_END))) C_MULTILINE_COMMENT_SUB_RE = re.compile( r"%s.*?%s" % (re.escape(C_COMMENT_START), re.escape(C_COMMENT_END)) ) CPP_CONDITIONAL_BLOCK_RE = re.compile(r"#(if( |def)|endif)") INCLUDE_RE = re.compile(r'#include (".+"|<.+>)') def GetNextStruct(filep): global CPP_DIRECT global LINE_COUNT got_struct = False have_c_comment = False data = "" while True: line = filep.readline() if not line: break LINE_COUNT += 1 line = line[:-1] if not have_c_comment and C_COMMENT_START_RE.search(line): if C_MULTILINE_COMMENT_SUB_RE.search(line): line = C_MULTILINE_COMMENT_SUB_RE.sub("", line) else: line = C_COMMENT_START_SUB_RE.sub("", line) have_c_comment = True if have_c_comment: if not C_COMMENT_END_RE.search(line): continue have_c_comment = False line = C_COMMENT_END_SUB_RE.sub("", line) line = NormalizeLine(line) if not line: continue if not got_struct: if INCLUDE_RE.match(line): CPP_DIRECT.append(line) elif CPP_CONDITIONAL_BLOCK_RE.match(line): CPP_DIRECT.append(line) elif PREPROCESSOR_DEF_RE.match(line): HEADER_DIRECT.append(line) elif not STRUCT_DEF_RE.match(line): raise RpcGenError("Missing struct on line %d: %s" % (LINE_COUNT, line)) else: got_struct = True data += line continue # We are inside the struct tokens = line.split("}") if len(tokens) == 1: data += " " + line continue if tokens[1]: raise RpcGenError("Trailing garbage after struct on line %d" % LINE_COUNT) # We found the end of the struct data += " %s}" % tokens[0] break # Remove any comments, that might be in there data = re.sub(r"/\*.*\*/", "", data) return data def Parse(factory, filep): """ Parses the input file and returns C code and corresponding header file. """ entities = [] while 1: # Just gets the whole struct nicely formatted data = GetNextStruct(filep) if not data: break entities.extend(ProcessStruct(factory, data)) return entities class CCodeGenerator(object): def __init__(self): pass @staticmethod def GuardName(name): # Use the complete provided path to the input file, with all # non-identifier characters replaced with underscores, to # reduce the chance of a collision between guard macros. return "EVENT_RPCOUT_%s_" % (NONIDENT_RE.sub("_", name).upper()) def HeaderPreamble(self, name): guard = self.GuardName(name) pre = """ /* * Automatically generated from %s */ #ifndef %s #define %s """ % ( name, guard, guard, ) if HEADER_DIRECT: for statement in HEADER_DIRECT: pre += "%s\n" % statement pre += "\n" pre += """ #include /* for ev_uint*_t */ #include """ return pre def HeaderPostamble(self, name): guard = self.GuardName(name) return "#endif /* %s */" % (guard) @staticmethod def BodyPreamble(name, header_file): global _NAME global _VERSION slash = header_file.rfind("/") if slash != -1: header_file = header_file[slash + 1 :] pre = """ /* * Automatically generated from %(name)s * by %(script_name)s/%(script_version)s. DO NOT EDIT THIS FILE. */ #include #include #include #include #include #include #include #if defined(EVENT__HAVE___func__) # ifndef __func__ # define __func__ __func__ # endif #elif defined(EVENT__HAVE___FUNCTION__) # define __func__ __FUNCTION__ #else # define __func__ __FILE__ #endif """ % { "name": name, "script_name": _NAME, "script_version": _VERSION, } for statement in CPP_DIRECT: pre += "%s\n" % statement pre += '\n#include "%s"\n\n' % header_file pre += "void event_warn(const char *fmt, ...);\n" pre += "void event_warnx(const char *fmt, ...);\n\n" return pre @staticmethod def HeaderFilename(filename): return ".".join(filename.split(".")[:-1]) + ".h" @staticmethod def CodeFilename(filename): return ".".join(filename.split(".")[:-1]) + ".gen.c" @staticmethod def Struct(name): return StructCCode(name) @staticmethod def EntryBytes(entry_type, name, tag, fixed_length): return EntryBytes(entry_type, name, tag, fixed_length) @staticmethod def EntryVarBytes(entry_type, name, tag): return EntryVarBytes(entry_type, name, tag) @staticmethod def EntryInt(entry_type, name, tag, bits=32): return EntryInt(entry_type, name, tag, bits) @staticmethod def EntryString(entry_type, name, tag): return EntryString(entry_type, name, tag) @staticmethod def EntryStruct(entry_type, name, tag, struct_name): return EntryStruct(entry_type, name, tag, struct_name) @staticmethod def EntryArray(entry): return EntryArray(entry) class CommandLine(object): def __init__(self, argv=None): """Initialize a command-line to launch event_rpcgen, as if from a command-line with CommandLine(sys.argv). If you're calling this directly, remember to provide a dummy value for sys.argv[0] """ global QUIETLY self.filename = None self.header_file = None self.impl_file = None self.factory = CCodeGenerator() parser = argparse.ArgumentParser( usage="%(prog)s [options] rpc-file [[h-file] c-file]" ) parser.add_argument("--quiet", action="store_true", default=False) parser.add_argument("rpc_file", type=argparse.FileType("r")) args, extra_args = parser.parse_known_args(args=argv) QUIETLY = args.quiet if extra_args: if len(extra_args) == 1: self.impl_file = extra_args[0].replace("\\", "/") elif len(extra_args) == 2: self.header_file = extra_args[0].replace("\\", "/") self.impl_file = extra_args[1].replace("\\", "/") else: parser.error("Spurious arguments provided") self.rpc_file = args.rpc_file if not self.impl_file: self.impl_file = self.factory.CodeFilename(self.rpc_file.name) if not self.header_file: self.header_file = self.factory.HeaderFilename(self.impl_file) if not self.impl_file.endswith(".c"): parser.error("can only generate C implementation files") if not self.header_file.endswith(".h"): parser.error("can only generate C header files") def run(self): filename = self.rpc_file.name header_file = self.header_file impl_file = self.impl_file factory = self.factory declare('Reading "%s"' % filename) with self.rpc_file: entities = Parse(factory, self.rpc_file) declare('... creating "%s"' % header_file) with open(header_file, "w") as header_fp: header_fp.write(factory.HeaderPreamble(filename)) # Create forward declarations: allows other structs to reference # each other for entry in entities: entry.PrintForwardDeclaration(header_fp) header_fp.write("\n") for entry in entities: entry.PrintTags(header_fp) entry.PrintDeclaration(header_fp) header_fp.write(factory.HeaderPostamble(filename)) declare('... creating "%s"' % impl_file) with open(impl_file, "w") as impl_fp: impl_fp.write(factory.BodyPreamble(filename, header_file)) for entry in entities: entry.PrintCode(impl_fp) def main(argv=None): try: CommandLine(argv=argv).run() return 0 except RpcGenError as e: sys.stderr.write(e) except EnvironmentError as e: if e.filename and e.strerror: sys.stderr.write("%s: %s" % (e.filename, e.strerror)) elif e.strerror: sys.stderr.write(e.strerror) else: raise return 1 if __name__ == "__main__": sys.exit(main(argv=sys.argv[1:])) libevent-2.1.13-stable/arc4random.c0000644000175000017500000003051515215775075016122 0ustar00nickmnickm/* Portable arc4random.c based on arc4random.c from OpenBSD. * Portable version by Chris Davis, adapted for Libevent by Nick Mathewson * Copyright (c) 2010 Chris Davis, Niels Provos, and Nick Mathewson * Copyright (c) 2010-2012 Niels Provos and Nick Mathewson * * Note that in Libevent, this file isn't compiled directly. Instead, * it's included from evutil_rand.c */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Arc4 random number generator for OpenBSD. * * This code is derived from section 17.1 of Applied Cryptography, * second edition, which describes a stream cipher allegedly * compatible with RSA Labs "RC4" cipher (the actual description of * which is a trade secret). The same algorithm is used as a stream * cipher called "arcfour" in Tatu Ylonen's ssh package. * * Here the stream cipher has been modified always to include the time * when initializing the state. That makes it impossible to * regenerate the same random sequence twice, so this can't be used * for encryption, but will generate good random numbers. * * RC4 is a registered trademark of RSA Laboratories. */ #ifndef ARC4RANDOM_EXPORT #define ARC4RANDOM_EXPORT #endif #ifndef ARC4RANDOM_UINT32 #define ARC4RANDOM_UINT32 uint32_t #endif #ifndef ARC4RANDOM_NO_INCLUDES #include "evconfig-private.h" #ifdef _WIN32 #include #include #include #else #include #include #include #include #ifdef EVENT__HAVE_SYS_SYSCTL_H #include #endif #ifdef EVENT__HAVE_SYS_RANDOM_H #include #endif #endif #include #include #include #endif /* Add platform entropy 32 bytes (256 bits) at a time. */ #define ADD_ENTROPY 32 /* Re-seed from the platform RNG after generating this many bytes. */ #define BYTES_BEFORE_RESEED 1600000 struct arc4_stream { unsigned char i; unsigned char j; unsigned char s[256]; }; #ifdef _WIN32 #define getpid _getpid #define pid_t int #endif static int rs_initialized; static struct arc4_stream rs; static pid_t arc4_stir_pid; static int arc4_count; static inline unsigned char arc4_getbyte(void); static inline void arc4_init(void) { int n; for (n = 0; n < 256; n++) rs.s[n] = n; rs.i = 0; rs.j = 0; } static inline void arc4_addrandom(const unsigned char *dat, int datlen) { int n; unsigned char si; rs.i--; for (n = 0; n < 256; n++) { rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si + dat[n % datlen]); rs.s[rs.i] = rs.s[rs.j]; rs.s[rs.j] = si; } rs.j = rs.i; } #ifndef _WIN32 static ssize_t read_all(int fd, unsigned char *buf, size_t count) { size_t numread = 0; ssize_t result; while (numread < count) { result = read(fd, buf+numread, count-numread); if (result<0) return -1; else if (result == 0) break; numread += result; } return (ssize_t)numread; } #endif #ifdef _WIN32 #define TRY_SEED_WIN32 static int arc4_seed_win32(void) { /* This is adapted from Tor's crypto_seed_rng() */ static int provider_set = 0; static HCRYPTPROV provider; unsigned char buf[ADD_ENTROPY]; if (!provider_set) { if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { if (GetLastError() != (DWORD)NTE_BAD_KEYSET) return -1; } provider_set = 1; } if (!CryptGenRandom(provider, sizeof(buf), buf)) return -1; arc4_addrandom(buf, sizeof(buf)); evutil_memclear_(buf, sizeof(buf)); return 0; } #endif #if defined(EVENT__HAVE_GETRANDOM) #define TRY_SEED_GETRANDOM static int arc4_seed_getrandom(void) { unsigned char buf[ADD_ENTROPY]; size_t len, n; unsigned i; int any_set; memset(buf, 0, sizeof(buf)); for (len = 0; len < sizeof(buf); len += n) { n = sizeof(buf) - len; if (0 == getrandom(&buf[len], n, 0)) return -1; } /* make sure that the buffer actually got set. */ for (i=0,any_set=0; i sizeof(buf)) n = len - sizeof(buf); if (sysctl(mib, 2, &buf[len], &n, NULL, 0) == -1) return -1; } } /* make sure that the buffer actually got set. */ for (i=any_set=0; i 0xffffffffUL) min = 0x100000000UL % upper_bound; #else /* Calculate (2**32 % upper_bound) avoiding 64-bit math */ if (upper_bound > 0x80000000) min = 1 + ~upper_bound; /* 2**32 - upper_bound */ else { /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */ min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound; } #endif /* * This could theoretically loop forever but each retry has * p > 0.5 (worst case, usually far better) of selecting a * number inside the range we need, so it should rarely need * to re-roll. */ for (;;) { r = arc4random(); if (r >= min) break; } return r % upper_bound; } #endif libevent-2.1.13-stable/LICENSE0000644000175000017500000001066015215775075014730 0ustar00nickmnickmLibevent is available for use under the following license, commonly known as the 3-clause (or "modified") BSD license: ============================== Copyright (c) 2000-2007 Niels Provos Copyright (c) 2007-2012 Niels Provos and Nick Mathewson Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================== Portions of Libevent are based on works by others, also made available by them under the three-clause BSD license above. The copyright notices are available in the corresponding source files; the license is as above. Here's a list: log.c: Copyright (c) 2000 Dug Song Copyright (c) 1993 The Regents of the University of California. strlcpy.c: Copyright (c) 1998 Todd C. Miller win32select.c: Copyright (c) 2003 Michael A. Davis evport.c: Copyright (c) 2007 Sun Microsystems ht-internal.h: Copyright (c) 2002 Christopher Clark minheap-internal.h: Copyright (c) 2006 Maxim Yegorushkin ============================== The arc4module is available under the following, sometimes called the "OpenBSD" license: Copyright (c) 1996, David Mazieres Copyright (c) 2008, Damien Miller Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ============================== The Windows timer code is based on code from libutp, which is distributed under this license, sometimes called the "MIT" license. Copyright (c) 2010 BitTorrent, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libevent-2.1.13-stable/libevent_extra.pc.in0000644000175000017500000000043415215775075017665 0ustar00nickmnickm#libevent pkg-config source file prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libevent_extra Description: libevent_extra Version: @VERSION@ Requires: Conflicts: Libs: -L${libdir} -levent_extra Libs.private: @LIBS@ Cflags: -I${includedir} libevent-2.1.13-stable/event_tagging.c0000644000175000017500000003470315221012604016667 0ustar00nickmnickm/* * Copyright (c) 2003-2009 Niels Provos * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "event2/event-config.h" #include "evconfig-private.h" #ifdef EVENT__HAVE_SYS_TYPES_H #include #endif #ifdef EVENT__HAVE_SYS_PARAM_H #include #endif #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #undef WIN32_LEAN_AND_MEAN #endif #ifdef EVENT__HAVE_SYS_IOCTL_H #include #endif #include #ifdef EVENT__HAVE_SYS_TIME_H #include #endif #include #include #include #include #ifndef _WIN32 #include #endif #ifdef EVENT__HAVE_UNISTD_H #include #endif #include #include "event2/event.h" #include "event2/tag.h" #include "event2/buffer.h" #include "log-internal.h" #include "mm-internal.h" #include "util-internal.h" /* Here's our wire format: Stream = TaggedData* TaggedData = Tag Length Data where the integer value of 'Length' is the length of 'data'. Tag = HByte* LByte where HByte is a byte with the high bit set, and LByte is a byte with the high bit clear. The integer value of the tag is taken by concatenating the lower 7 bits from all the tags. So for example, the tag 0x66 is encoded as [66], whereas the tag 0x166 is encoded as [82 66] Length = Integer Integer = NNibbles Nibble* Padding? where NNibbles is a 4-bit value encoding the number of nibbles-1, and each Nibble is 4 bits worth of encoded integer, in big-endian order. If the total encoded integer size is an odd number of nibbles, a final padding nibble with value 0 is appended. */ EVENT2_EXPORT_SYMBOL int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf); EVENT2_EXPORT_SYMBOL int evtag_decode_int64(ev_uint64_t *pnumber, struct evbuffer *evbuf); EVENT2_EXPORT_SYMBOL int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag); EVENT2_EXPORT_SYMBOL int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf); void evtag_init(void) { } /* * We encode integers by nibbles; the first nibble contains the number * of significant nibbles - 1; this allows us to encode up to 64-bit * integers. This function is byte-order independent. * * @param number a 32-bit unsigned integer to encode * @param data a pointer to where the data should be written. Must * have at least 5 bytes free. * @return the number of bytes written into data. */ #define ENCODE_INT_INTERNAL(data, number) do { \ int off = 1, nibbles = 0; \ \ memset(data, 0, sizeof(number)+1); \ while (number) { \ if (off & 0x1) \ data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f); \ else \ data[off/2] = (data[off/2] & 0x0f) | \ ((number & 0x0f) << 4); \ number >>= 4; \ off++; \ } \ \ if (off > 2) \ nibbles = off - 2; \ \ /* Off - 1 is the number of encoded nibbles */ \ data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4); \ \ return ((off + 1) / 2); \ } while (0) static inline int encode_int_internal(ev_uint8_t *data, ev_uint32_t number) { ENCODE_INT_INTERNAL(data, number); } static inline int encode_int64_internal(ev_uint8_t *data, ev_uint64_t number) { ENCODE_INT_INTERNAL(data, number); } void evtag_encode_int(struct evbuffer *evbuf, ev_uint32_t number) { ev_uint8_t data[5]; int len = encode_int_internal(data, number); evbuffer_add(evbuf, data, len); } void evtag_encode_int64(struct evbuffer *evbuf, ev_uint64_t number) { ev_uint8_t data[9]; int len = encode_int64_internal(data, number); evbuffer_add(evbuf, data, len); } /* * Support variable length encoding of tags; we use the high bit in each * octet as a continuation signal. */ int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag) { int bytes = 0; ev_uint8_t data[5]; memset(data, 0, sizeof(data)); do { ev_uint8_t lower = tag & 0x7f; tag >>= 7; if (tag) lower |= 0x80; data[bytes++] = lower; } while (tag); if (evbuf != NULL) evbuffer_add(evbuf, data, bytes); return (bytes); } static int decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain) { ev_uint32_t number = 0; size_t len = evbuffer_get_length(evbuf); ev_uint8_t *data; size_t count = 0; int shift = 0, done = 0; /* * the encoding of a number is at most one byte more than its * storage size. however, it may also be much smaller. */ size_t pullup_len = len < sizeof(number) + 1 ? len : sizeof(number) + 1; data = evbuffer_pullup( evbuf, pullup_len); if (!data) return (-1); while (count++ < pullup_len) { ev_uint8_t lower = *data++; if (shift >= 28) { /* Make sure it fits into 32 bits */ if (shift > 28) return (-1); if ((lower & 0x7f) > 15) return (-1); } number |= (lower & (unsigned)0x7f) << shift; shift += 7; if (!(lower & 0x80)) { done = 1; break; } } if (!done) return (-1); if (dodrain) evbuffer_drain(evbuf, count); if (ptag != NULL) *ptag = number; return count > INT_MAX ? INT_MAX : (int)(count); } int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf) { return (decode_tag_internal(ptag, evbuf, 1 /* dodrain */)); } /* * Marshal a data type, the general format is as follows: * * tag number: one byte; length: var bytes; payload: var bytes */ void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, ev_uint32_t len) { evtag_encode_tag(evbuf, tag); evtag_encode_int(evbuf, len); evbuffer_add(evbuf, (void *)data, len); } void evtag_marshal_buffer(struct evbuffer *evbuf, ev_uint32_t tag, struct evbuffer *data) { evtag_encode_tag(evbuf, tag); /* XXX support more than UINT32_MAX data */ evtag_encode_int(evbuf, (ev_uint32_t)evbuffer_get_length(data)); evbuffer_add_buffer(evbuf, data); } /* Marshaling for integers */ void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer) { ev_uint8_t data[5]; int len = encode_int_internal(data, integer); evtag_encode_tag(evbuf, tag); evtag_encode_int(evbuf, len); evbuffer_add(evbuf, data, len); } void evtag_marshal_int64(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint64_t integer) { ev_uint8_t data[9]; int len = encode_int64_internal(data, integer); evtag_encode_tag(evbuf, tag); evtag_encode_int(evbuf, len); evbuffer_add(evbuf, data, len); } void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string) { /* TODO support strings longer than UINT32_MAX ? */ evtag_marshal(buf, tag, string, (ev_uint32_t)strlen(string)); } void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv) { ev_uint8_t data[10]; int len = encode_int_internal(data, tv->tv_sec); len += encode_int_internal(data + len, tv->tv_usec); evtag_marshal(evbuf, tag, data, len); } #define DECODE_INT_INTERNAL(number, maxnibbles, pnumber, evbuf, offset) \ do { \ ev_uint8_t *data; \ ev_ssize_t len = evbuffer_get_length(evbuf) - offset; \ int nibbles = 0; \ \ if (len <= 0) \ return (-1); \ \ /* XXX(niels): faster? */ \ data = evbuffer_pullup(evbuf, offset + 1) + offset; \ if (!data) \ return (-1); \ \ nibbles = ((data[0] & 0xf0) >> 4) + 1; \ if (nibbles > maxnibbles || (nibbles >> 1) + 1 > len) \ return (-1); \ len = (nibbles >> 1) + 1; \ \ data = evbuffer_pullup(evbuf, offset + len) + offset; \ if (!data) \ return (-1); \ \ while (nibbles > 0) { \ number <<= 4; \ if (nibbles & 0x1) \ number |= data[nibbles >> 1] & 0x0f; \ else \ number |= (data[nibbles >> 1] & 0xf0) >> 4; \ nibbles--; \ } \ \ *pnumber = number; \ \ return (int)(len); \ } while (0) /* Internal: decode an integer from an evbuffer, without draining it. * Only integers up to 32-bits are supported. * * @param evbuf the buffer to read from * @param offset an index into the buffer at which we should start reading. * @param pnumber a pointer to receive the integer. * @return The length of the number as encoded, or -1 on error. */ static int decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int offset) { ev_uint32_t number = 0; DECODE_INT_INTERNAL(number, 8, pnumber, evbuf, offset); } static int decode_int64_internal(ev_uint64_t *pnumber, struct evbuffer *evbuf, int offset) { ev_uint64_t number = 0; DECODE_INT_INTERNAL(number, 16, pnumber, evbuf, offset); } int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf) { int res = decode_int_internal(pnumber, evbuf, 0); if (res != -1) evbuffer_drain(evbuf, res); return (res == -1 ? -1 : 0); } int evtag_decode_int64(ev_uint64_t *pnumber, struct evbuffer *evbuf) { int res = decode_int64_internal(pnumber, evbuf, 0); if (res != -1) evbuffer_drain(evbuf, res); return (res == -1 ? -1 : 0); } int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag) { return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */)); } int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength) { int res, len; len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); if (len == -1) return (-1); res = decode_int_internal(plength, evbuf, len); if (res == -1) return (-1); *plength += res + len; return (0); } int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength) { int res, len; len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); if (len == -1) return (-1); res = decode_int_internal(plength, evbuf, len); if (res == -1) return (-1); return (0); } /* just unmarshals the header and returns the length of the remaining data */ int evtag_unmarshal_header(struct evbuffer *evbuf, ev_uint32_t *ptag) { ev_uint32_t len; if (decode_tag_internal(ptag, evbuf, 1 /* dodrain */) == -1) return (-1); if (evtag_decode_int(&len, evbuf) == -1 || len > INT_MAX) return (-1); if (evbuffer_get_length(evbuf) < len) return (-1); return (len); } int evtag_consume(struct evbuffer *evbuf) { int len; if ((len = evtag_unmarshal_header(evbuf, NULL)) == -1) return (-1); evbuffer_drain(evbuf, len); return (0); } /* Reads the data type from an event buffer */ int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst) { int len; if ((len = evtag_unmarshal_header(src, ptag)) == -1) return (-1); if (evbuffer_add(dst, evbuffer_pullup(src, len), len) == -1) return (-1); evbuffer_drain(src, len); return (len); } /* Marshaling for integers */ int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, ev_uint32_t *pinteger) { ev_uint32_t tag; ev_uint32_t len; int result; if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1) return (-1); if (need_tag != tag) return (-1); if (evtag_decode_int(&len, evbuf) == -1) return (-1); if (evbuffer_get_length(evbuf) < len) return (-1); result = decode_int_internal(pinteger, evbuf, 0); evbuffer_drain(evbuf, len); if (result < 0 || (size_t)result > len) /* XXX Should this be != rather than > ?*/ return (-1); else return result; } int evtag_unmarshal_int64(struct evbuffer *evbuf, ev_uint32_t need_tag, ev_uint64_t *pinteger) { ev_uint32_t tag; ev_uint32_t len; int result; if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1) return (-1); if (need_tag != tag) return (-1); if (evtag_decode_int(&len, evbuf) == -1) return (-1); if (evbuffer_get_length(evbuf) < len) return (-1); result = decode_int64_internal(pinteger, evbuf, 0); evbuffer_drain(evbuf, len); if (result < 0 || (size_t)result > len) /* XXX Should this be != rather than > ?*/ return (-1); else return result; } /* Unmarshal a fixed length tag */ int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data, size_t len) { ev_uint32_t tag; int tag_len; /* Now unmarshal a tag and check that it matches the tag we want */ if ((tag_len = evtag_unmarshal_header(src, &tag)) < 0 || tag != need_tag) return (-1); if ((size_t)tag_len != len) return (-1); evbuffer_remove(src, data, len); return (0); } int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, char **pstring) { ev_uint32_t tag; int tag_len; if ((tag_len = evtag_unmarshal_header(evbuf, &tag)) == -1 || tag != need_tag) return (-1); *pstring = mm_malloc(tag_len + 1); if (*pstring == NULL) { event_warn("%s: malloc", __func__); return -1; } evbuffer_remove(evbuf, *pstring, tag_len); (*pstring)[tag_len] = '\0'; return (0); } int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, struct timeval *ptv) { ev_uint32_t tag; ev_uint32_t integer; int len, offset, offset2; int result = -1; if ((len = evtag_unmarshal_header(evbuf, &tag)) == -1) return (-1); if (tag != need_tag) goto done; if ((offset = decode_int_internal(&integer, evbuf, 0)) == -1) goto done; ptv->tv_sec = integer; if ((offset2 = decode_int_internal(&integer, evbuf, offset)) == -1) goto done; ptv->tv_usec = integer; if (offset + offset2 > len) /* XXX Should this be != instead of > ? */ goto done; result = 0; done: evbuffer_drain(evbuf, len); return result; } libevent-2.1.13-stable/evutil_rand.c0000644000175000017500000001233515215775075016404 0ustar00nickmnickm/* * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This file has our secure PRNG code. On platforms that have arc4random(), * we just use that. Otherwise, we include arc4random.c as a bunch of static * functions, and wrap it lightly. We don't expose the arc4random*() APIs * because A) they aren't in our namespace, and B) it's not nice to name your * APIs after their implementations. We keep them in a separate file * so that other people can rip it out and use it for whatever. */ #include "event2/event-config.h" #include "evconfig-private.h" #include #include "util-internal.h" #include "evthread-internal.h" #ifdef EVENT__HAVE_ARC4RANDOM #include #include int evutil_secure_rng_set_urandom_device_file(char *fname) { (void) fname; return -1; } int evutil_secure_rng_init(void) { /* call arc4random() now to force it to self-initialize */ (void) arc4random(); return 0; } #ifndef EVENT__DISABLE_THREAD_SUPPORT int evutil_secure_rng_global_setup_locks_(const int enable_locks) { return 0; } #endif static void evutil_free_secure_rng_globals_locks(void) { } static void ev_arc4random_buf(void *buf, size_t n) { #if defined(EVENT__HAVE_ARC4RANDOM_BUF) && !defined(__APPLE__) arc4random_buf(buf, n); return; #else unsigned char *b = buf; #if defined(EVENT__HAVE_ARC4RANDOM_BUF) /* OSX 10.7 introducd arc4random_buf, so if you build your program * there, you'll get surprised when older versions of OSX fail to run. * To solve this, we can check whether the function pointer is set, * and fall back otherwise. (OSX does this using some linker * trickery.) */ { void (*tptr)(void *,size_t) = (void (*)(void*,size_t))arc4random_buf; if (tptr != NULL) { arc4random_buf(buf, n); return; } } #endif /* Make sure that we start out with b at a 4-byte alignment; plenty * of CPUs care about this for 32-bit access. */ if (n >= 4 && ((ev_uintptr_t)b) & 3) { ev_uint32_t u = arc4random(); int n_bytes = 4 - (((ev_uintptr_t)b) & 3); memcpy(b, &u, n_bytes); b += n_bytes; n -= n_bytes; } while (n >= 4) { *(ev_uint32_t*)b = arc4random(); b += 4; n -= 4; } if (n) { ev_uint32_t u = arc4random(); memcpy(b, &u, n); } #endif } #else /* !EVENT__HAVE_ARC4RANDOM { */ #ifdef EVENT__ssize_t #define ssize_t EVENT__ssize_t #endif #define ARC4RANDOM_EXPORT static #define ARC4_LOCK_() EVLOCK_LOCK(arc4rand_lock, 0) #define ARC4_UNLOCK_() EVLOCK_UNLOCK(arc4rand_lock, 0) #ifndef EVENT__DISABLE_THREAD_SUPPORT static void *arc4rand_lock; #endif #define ARC4RANDOM_UINT32 ev_uint32_t #define ARC4RANDOM_NOSTIR #define ARC4RANDOM_NORANDOM #define ARC4RANDOM_NOUNIFORM #include "./arc4random.c" #ifndef EVENT__DISABLE_THREAD_SUPPORT int evutil_secure_rng_global_setup_locks_(const int enable_locks) { EVTHREAD_SETUP_GLOBAL_LOCK(arc4rand_lock, 0); return 0; } #endif static void evutil_free_secure_rng_globals_locks(void) { #ifndef EVENT__DISABLE_THREAD_SUPPORT if (arc4rand_lock != NULL) { EVTHREAD_FREE_LOCK(arc4rand_lock, 0); arc4rand_lock = NULL; } #endif return; } int evutil_secure_rng_set_urandom_device_file(char *fname) { #ifdef TRY_SEED_URANDOM ARC4_LOCK_(); arc4random_urandom_filename = fname; ARC4_UNLOCK_(); #endif return 0; } int evutil_secure_rng_init(void) { int val; ARC4_LOCK_(); val = (!arc4_stir()) ? 0 : -1; ARC4_UNLOCK_(); return val; } static void ev_arc4random_buf(void *buf, size_t n) { arc4random_buf(buf, n); } #endif /* } !EVENT__HAVE_ARC4RANDOM */ void evutil_secure_rng_get_bytes(void *buf, size_t n) { ev_arc4random_buf(buf, n); } #if !defined(EVENT__HAVE_ARC4RANDOM) || defined(EVENT__HAVE_ARC4RANDOM_ADDRANDOM) void evutil_secure_rng_add_bytes(const char *buf, size_t n) { arc4random_addrandom((unsigned char*)buf, n>(size_t)INT_MAX ? INT_MAX : (int)n); } #endif void evutil_free_secure_rng_globals_(void) { evutil_free_secure_rng_globals_locks(); } libevent-2.1.13-stable/whatsnew-2.1.txt0000644000175000017500000010115315215775075016620 0ustar00nickmnickm What's new in Libevent 2.1 Nick Mathewson 0. Before we start 0.1. About this document This document describes the key differences between Libevent 2.0 and Libevent 2.1, from a user's point of view. It's a work in progress. For better documentation about libevent, see the links at http://libevent.org/ Libevent 2.1 would not be possible without the generous help of numerous volunteers. For a list of who did what in Libevent 2.1, please see the ChangeLog! NOTE: I am very sure that I missed some thing on this list. Caveat haxxor. 0.2. Where to get help Try looking at the other documentation too. All of the header files have documentation in the doxygen format; this gets turned into nice HTML and linked to from the libevent.org website. There is a work-in-progress book with reference manual at http://www.wangafu.net/~nickm/libevent-book/ . You can ask questions on the #libevent IRC channel at irc.oftc.net or on the mailing list at libevent-users@freehaven.net. The mailing list is subscribers-only, so you will need to subscribe before you post. 0.3. Compatibility Our source-compatibility policy is that correct code (that is to say, code that uses public interfaces of Libevent and relies only on their documented behavior) should have forward source compatibility: any such code that worked with a previous version of Libevent should work with this version too. We don't try to do binary compatibility except within stable release series, so binaries linked against any version of Libevent 2.0 will probably need to be recompiled against Libevent 2.1.4-alpha if you want to use it. It is probable that we'll break binary compatibility again before Libevent 2.1 is stable. 1. New APIs and features 1.1. New ways to build libevent We now provide an --enable-gcc-hardening configure option to turn on GCC features designed for increased code security. There is also an --enable-silent-rules configure option to make compilation run more quietly with automake 1.11 or later. You no longer need to use the --enable-gcc-warnings option to turn on all of the GCC warnings that Libevent uses. The only change from using that option now is to turn warnings into errors. For IDE users, files that are not supposed to be built are now surrounded with appropriate #ifdef lines to keep your IDE from getting upset. There is now an alternative cmake-based build process; cmake users should see the relevant sections in the README. 1.2. New functions for events and the event loop If you're running Libevent with multiple event priorities, you might want to make sure that Libevent checks for new events frequently, so that time-consuming or numerous low-priority events don't keep it from checking for new high-priority events. You can now use the event_config_set_max_dispatch_interval() interface to ensure that the loop checks for new events either every N microseconds, every M callbacks, or both. When configuring an event base, you can now choose whether you want timers to be more efficient, or more precise. (This only has effect on Linux for now.) Timers are efficient by default: to select more precise timers, use the EVENT_BASE_FLAG_PRECISE_TIMER flag when constructing the event_config, or set the EVENT_PRECISE_TIMER environment variable to a non-empty string. There is an EVLOOP_NO_EXIT_ON_EMPTY flag that tells event_base_loop() to keep looping even when there are no pending events. (Ordinarily, event_base_loop() will exit as soon as no events are pending.) Past versions of Libevent have been annoying to use with some memory-leak-checking tools, because Libevent allocated some global singletons but provided no means to free them. There is now a function, libevent_global_shutdown(), that you can use to free all globally held resources before exiting, so that your leak-check tools don't complain. (Note: this function doesn't free non-global things like events, bufferevents, and so on; and it doesn't free anything that wouldn't otherwise get cleaned up by the operating system when your process exit()s. If you aren't using a leak-checking tool, there is not much reason to call libevent_global_shutdown().) There is a new event_base_get_npriorities() function to return the number of priorities set in the event base. Libevent 2.0 added an event_new() function to construct a new struct event on the heap. Unfortunately, with event_new(), there was no equivalent for: struct event ev; event_assign(&ev, base, fd, EV_READ, callback, &ev); In other words, there was no easy way for event_new() to set up an event so that the event itself would be its callback argument. Libevent 2.1 lets you do this by passing "event_self_cbarg()" as the callback argument: struct event *evp; evp = event_new(base, fd, EV_READ, callback, event_self_cbarg()); There's also a new event_base_get_running_event() function you can call from within a Libevent callback to get a pointer to the current event. This should never be strictly necessary, but it's sometimes convenient. The event_base_once() function used to leak some memory if the event that it added was never actually triggered. Now, its memory is tracked in the event_base and freed when the event_base is freed. Note however that Libevent doesn't know how to free any information passed as the callback argument to event_base_once is still something you'll might need a way to de-allocate yourself. There is an event_get_priority() function to return an event's priority. By analogy to event_base_loopbreak(), there is now an event_base_loopcontinue() that tells Libevent to stop processing active event callbacks, and re-scan for new events right away. There's a function, event_base_foreach_event(), that can iterate over every event currently pending or active on an event base, and invoke a user-supplied callback on each. The callback must not alter the events or add or remove anything to the event base. We now have an event_remove_timer() function to remove the timeout on an event while leaving its socket and/or signal triggers unchanged. (If we were designing the API from scratch, this would be the behavior of "event_add(ev, NULL)" on an already-added event with a timeout. But that's a no-op in past versions of Libevent, and we don't want to break compatibility.) You can use the new event_base_get_num_events() function to find the number of events active or pending on an event_base. To find the largest number of events that there have been since the last call, use event_base_get_max_events(). You can now activate all the events waiting for a given fd or signal using the event_base_active_by_fd() and event_base_active_by_signal() APIs. On backends that support it (currently epoll), there is now an EV_CLOSED flag that programs can use to detect when a socket has closed without having to read all the bytes until receiving an EOF. 1.3. Event finalization 1.3.1. Why event finalization? Libevent 2.1 now supports an API for safely "finalizing" events that might be running in multiple threads, and provides a way to slightly change the semantics of event_del() to prevent deadlocks in multithreaded programs. To motivate this feature, consider the following code, in the context of a mulithreaded Libevent application: struct connection *conn = event_get_callback_arg(ev); event_del(ev); connection_free(conn); Suppose that the event's callback might be running in another thread, and using the value of "conn" concurrently. We wouldn't want to execute the connection_free() call until "conn" is no longer in use. How can we make this code safe? Libevent 2.0 answered that question by saying that the event_del() call should block if the event's callback is running in another thread. That way, we can be sure that event_del() has canceled the callback (if the callback hadn't started running yet), or has waited for the callback to finish. But now suppose that the data structure is protected by a lock, and we have the following code: void check_disable(struct connection *connection) { lock(connection); if (should_stop_reading(connection)) event_del(connection->read_event); unlock(connection); } What happens when we call check_disable() from a callback and from another thread? Let's say that the other thread gets the lock first. If it decides to call event_del(), it will wait for the callback to finish. But meanwhile, the callback will be waiting for the lock on the connection. Since each threads is waiting for the other one to release a resource, the program will deadlock. This bug showed up in multithreaded bufferevent programs in 2.1, particularly when freeing bufferevents. (For more information, see the "Deadlock when calling bufferevent_free from an other thread" thread on libevent-users starting on 6 August 2012 and running through February of 2013. You might also like to read my earlier writeup at http://archives.seul.org/libevent/users/Feb-2012/msg00053.html and the ensuing discussion.) 1.3.2. The EV_FINALIZE flag and avoiding deadlock To prevent the deadlock condition described above, Libevent 2.1.3-alpha adds a new flag, "EV_FINALIZE". You can pass it to event_new() and event_assign() along with EV_READ, EV_WRITE, and the other event flags. When an event is constructed with the EV_FINALIZE flag, event_del() will not block on that event, even when the event's callback is running in another thread. By using EV_FINALIZE, you are therefore promising not to use the "event_del(ev); free(event_get_callback_arg(ev));" pattern, but rather to use one of the finalization functions below to clean up the event. EV_FINALIZE has no effect on a single-threaded program, or on a program where events are only used from one thread. There are also two new variants of event_del() that you can use for more fine-grained control: event_del_noblock(ev) event_del_block(ev) The event_del_noblock() function will never block, even if the event callback is running in another thread and doesn't have the EV_FINALIZE flag. The event_del_block() function will _always_ block if the event callback is running in another thread, even if the event _does_ have the EV_FINALIZE flag. [A future version of Libevent may have a way to make the EV_FINALIZE flag the default.] 1.3.3. Safely finalizing events To safely tear down an event that may be running, Libevent 2.1.3-alpha introduces event_finalize() and event_free_finalize(). You call them on an event, and provide a finalizer callback to be run on the event and its callback argument once the event is definitely no longer running. With event_free_finalize(), the event is also freed once the finalizer callback has been invoked. A finalized event cannot be re-added or activated. The finalizer callback must not add events, activate events, or attempt to "resucitate" the event being finalized in any way. If any finalizer callbacks are pending as the event_base is being freed, they will be invoked. You can override this behavior with the new function event_base_free_nofinalize(). 1.4. New debugging features You can now turn on debug logs at runtime using a new function, event_enable_debug_logging(). The event_enable_lock_debugging() function is now spelled correctly. You can still use the old "event_enable_lock_debuging" name, though, so your old programs shouldnt' break. There's also been some work done to try to make the debugging logs more generally useful. 1.5. New evbuffer functions In Libevent 2.0, we introduced evbuffer_add_file() to add an entire file's contents to an evbuffer, and then send them using sendfile() or mmap() as appropriate. This API had some drawbacks, however. Notably, it created one mapping or fd for every instance of the same file added to any evbuffer. Also, adding a file to an evbuffer could make that buffer unusable with SSL bufferevents, filtering bufferevents, and any code that tried to read the contents of the evbuffer. Libevent 2.1 adds a new evbuffer_file_segment API to solve these problems. Now, you can use evbuffer_file_segment_new() to construct a file-segment object, and evbuffer_add_file_segment() to insert it (or part of it) into an evbuffer. These segments avoid creating redundant maps or fds. Better still, the code is smart enough (when the OS supports sendfile) to map the file when that's necessary, and use sendfile() otherwise. File segments can receive callback functions that are invoked when the file segments are freed. The evbuffer_ptr interface has been extended so that an evbuffer_ptr can now yield a point just after the end of the buffer. This makes many algorithms simpler to implement. There's a new evbuffer_add_buffer() interface that you can use to add one buffer to another nondestructively. When you say evbuffer_add_buffer_reference(outbuf, inbuf), outbuf now contains a reference to the contents of inbuf. To aid in adding data in bulk while minimizing evbuffer calls, there is an evbuffer_add_iovec() function. There's a new evbuffer_copyout_from() variant function to enable copying data nondestructively from the middle of a buffer. evbuffer_readln() now supports an EVBUFFER_EOL_NUL argument to fetch NUL-terminated strings from buffers. There's a new evbuffer_set_flags()/evbuffer_clear_flags() that you can use to set EVBUFFER_FLAG_DRAINS_TO_FD. 1.6. New functions and features: bufferevents You can now use the bufferevent_getcb() function to find out a bufferevent's callbacks. Previously, there was no supported way to do that. The largest chunk readable or writeable in a single bufferevent callback is no longer hardcoded; it's now configurable with the new functions bufferevent_set_max_single_read() and bufferevent_set_max_single_write(). For consistency, OpenSSL bufferevents now make sure to always set one of BEV_EVENT_READING or BEV_EVENT_WRITING when invoking an event callback. Calling bufferevent_set_timeouts(bev, NULL, NULL) now removes the timeouts from socket and ssl bufferevents correctly. You can find the priority at which a bufferevent runs with bufferevent_get_priority(). The function bufferevent_get_token_bucket_cfg() can retrieve the rate-limit settings for a bufferevent; bufferevent_getwatermark() can return a bufferevent's current watermark settings. You can manually trigger a bufferevent's callbacks via bufferevent_trigger() and bufferevent_trigger_event(). Also you can manually increment/decrement reference for bufferevent with bufferevent_incref()/bufferevent_decref(), it is useful in situations where a user may reference the bufferevent somewhere else. Now bufferevent_openssl supports "dirty" shutdown (when the peer closes the TCP connection before closing the SSL channel), see bufferevent_openssl_get_allow_dirty_shutdown() and bufferevent_openssl_set_allow_dirty_shutdown(). And also libevent supports openssl 1.1. 1.7. New functions and features: evdns The previous evdns interface used an "open a test UDP socket" trick in order to detect IPv6 support. This was a hack, since it would sometimes badly confuse people's firewall software, even though no packets were sent. The current evdns interface-detection code uses the appropriate OS functions to see which interfaces are configured. The evdns_base_new() function now has multiple possible values for its second (flags) argument. Using 1 and 0 have their old meanings, though the 1 flag now has a symbolic name of EVDNS_BASE_INITIALIZE_NAMESERVERS. A second flag is now supported too: the EVDNS_BASE_DISABLE_WHEN_INACTIVE flag, which tells the evdns_base that it should not prevent Libevent from exiting while it has no DNS requests in progress. There is a new evdns_base_clear_host_addresses() function to remove all the /etc/hosts addresses registered with an evdns instance. Also there is evdns_base_get_nameserver_addr() for retrieve the address of the 'idx'th configured nameserver. 1.8. New functions and features: evconnlistener Libevent 2.1 adds the following evconnlistener flags: LEV_OPT_DEFERRED_ACCEPT -- Tells the OS that it doesn't need to report sockets as having arrived until the initiator has sent some data too. This can greatly improve performance with protocols like HTTP where the client always speaks first. On operating systems that don't support this functionality, this option has no effect. LEV_OPT_REUSEABLE_PORT -- Indicates that we ask to allow multiple servers to bind to the same port if they each set the option Ionly on Linux and >=3.9) LEV_OPT_DISABLED -- Creates an evconnlistener in the disabled (not listening) state. Libevent 2.1 changes the behavior of the LEV_OPT_CLOSE_ON_EXEC flag. Previously, it would apply to the listener sockets, but not to the accepted sockets themselves. That's almost never what you want. Now, it applies both to the listener and the accepted sockets. 1.9. New functions and features: evhttp ********************************************************************** NOTE: The evhttp module will eventually be deprecated in favor of Mark Ellzey's libevhtp library. Don't worry -- this won't happen until libevhtp provides every feature that evhttp does, and provides a compatible interface that applications can use to migrate. ********************************************************************** Previously, you could only set evhttp timeouts in increments of one second. Now, you can use evhttp_set_timeout_tv() and evhttp_connection_set_timeout_tv() to configure microsecond-granularity timeouts. Also there is evhttp_connection_set_initial_retry_tv() to change initial retry timeout. There are a new pair of functions: evhttp_set_bevcb() and evhttp_connection_base_bufferevent_new(), that you can use to configure which bufferevents will be used for incoming and outgoing http connections respectively. These functions, combined with SSL bufferevents, should enable HTTPS support. There's a new evhttp_foreach_bound_socket() function to iterate over every listener on an evhttp object. Whitespace between lines in headers is now folded into a single space; whitespace at the end of a header is now removed. The socket errno value is now preserved when invoking an http error callback. There's a new kind of request callback for errors; you can set it with evhttp_request_set_error_cb(). It gets called when there's a request error, and actually reports the error code and lets you figure out which request failed. You can navigate from an evhttp_connection back to its evhttp with the new evhttp_connection_get_server() function. You can override the default HTTP Content-Type with the new evhttp_set_default_content_type() function There's a new evhttp_connection_get_addr() API to return the peer address of an evhttp_connection. The new evhttp_send_reply_chunk_with_cb() is a variant of evhttp_send_reply_chunk() with a callback to be invoked when the chunk is sent. The evhttp_request_set_header_cb() facility adds a callback to be invoked while parsing headers. The evhttp_request_set_on_complete_cb() facility adds a callback to be invoked on request completion. You can add linger-close for http server by passing EVHTTP_SERVER_LINGERING_CLOSE to evhttp_set_flags(), with this flag server read all the clients body, and only after this respond with an error if the clients body exceed max_body_size (since some clients cannot read response otherwise). The evhttp_connection_set_family() can bypass family hint to evdns. There are some flags available for connections, which can be installed with evhttp_connection_set_flags(): - EVHTTP_CON_REUSE_CONNECTED_ADDR -- reuse connection address on retry (avoid extra DNS request). - EVHTTP_CON_READ_ON_WRITE_ERROR - try read error, since server may already close the connection. The evhttp_connection_free_on_completion() can be used to tell libevent to free the connection object after the last request has completed or failed. There is evhttp_request_get_response_code_line() if evhttp_request_get_response_code() is not enough for you. There are *evhttp_uri_parse_with_flags() that accepts EVHTTP_URI_NONCONFORMANT to tolerate URIs that do not conform to RFC3986. The evhttp_uri_set_flags() can changes the flags on URI. 1.10. New functions and features: evutil There's a function "evutil_secure_rng_set_urandom_device_file()" that you can use to override the default file that Libevent uses to seed its (sort-of) secure RNG. The evutil_date_rfc1123() returns date in RFC1123 There are new API to work with monotonic timer -- monotonic time is guaranteed never to run in reverse, but is not necessarily epoch-based. Use it to make reliable measurements of elapsed time between events even when the system time may be changed: - evutil_monotonic_timer_new()/evutil_monotonic_timer_free() - evutil_configure_monotonic_time() - evutil_gettime_monotonic() Use evutil_make_listen_socket_reuseable_port() to set SO_REUSEPORT (linux >= 3.9) The evutil_make_tcp_listen_socket_deferred() can make a tcp listener socket defer accept()s until there is data to read (TCP_DEFER_ACCEPT). 2. Cross-platform performance improvements 2.1. Better data structures We replaced several users of the sys/queue.h "TAILQ" data structure with the "LIST" data structure. Because this data type doesn't require FIFO access, it requires fewer pointer checks and manipulations to keep it in line. All previous versions of Libevent have kept every pending (added) event in an "eventqueue" data structure. Starting in Libevent 2.0, however, this structure became redundant: every pending timeout event is stored in the timeout heap or in one of the common_timeout queues, and every pending fd or signal event is stored in an evmap. Libevent 2.1 removes this data structure, and thereby saves all of the code that we'd been using to keep it updated. 2.2. Faster activations and timeouts It's a common pattern in older code to use event_base_once() with a 0-second timeout to ensure that a callback will get run 'as soon as possible' in the current iteration of the Libevent loop. We optimize this case by calling event_active() directly, and bypassing the timeout pool. (People who are using this pattern should also consider using event_active() themselves.) Libevent 2.0 would wake up a polling event loop whenever the first timeout in the event loop was adjusted--whether it had become earlier or later. We now only notify the event loop when a change causes the expiration time to become _sooner_ than it would have been otherwise. The timeout heap code is now optimized to perform fewer comparisons and shifts when changing or removing a timeout. Instead of checking for a wall-clock time jump every time we call clock_gettime(), we now check only every 5 seconds. This should save a huge number of gettimeofday() calls. 2.3. Microoptimizations Internal event list maintainance no longer use the antipattern where we have one function with multiple totally independent behaviors depending on an argument: #define OP1 1 #define OP2 2 #define OP3 3 void func(int operation, struct event *ev) { switch (op) { ... } } Instead, these functions are now split into separate functions for each operation: void func_op1(struct event *ev) { ... } void func_op2(struct event *ev) { ... } void func_op3(struct event *ev) { ... } This produces better code generation and inlining decisions on some compilers, and makes the code easier to read and check. 2.4. Evbuffer performance improvements The EVBUFFER_EOL_CRLF line-ending type is now much faster, thanks to smart optimizations. 2.5. HTTP performance improvements o Performance tweak to evhttp_parse_request_line. (aee1a97 Mark Ellzey) o Add missing break to evhttp_parse_request_line (0fcc536) 2.6. Coarse timers by default on Linux Due to limitations of the epoll interface, Libevent programs using epoll have not previously been able to wait for timeouts with accuracy smaller than 1 millisecond. But Libevent had been using CLOCK_MONOTONIC for timekeeping on Linux, which is needlessly expensive: CLOCK_MONOTONIC_COARSE has approximately the resolution corresponding to epoll, and is much faster to invoke than CLOCK_MONOTONIC. To disable coarse timers, and get a more plausible precision, use the new EVENT_BASE_FLAG_PRECISE_TIMER flag when setting up your event base. 3. Backend/OS-specific improvements 3.1. Linux-specific improvements The logic for deciding which arguements to use with epoll_ctl() is now a table-driven lookup, rather than the previous pile of cascading branches. This should minimize epoll_ctl() calls and make the epoll code run a little faster on change-heavy loads. Libevent now takes advantage of Linux's support for enhanced APIs (e.g., SOCK_CLOEXEC, SOCK_NONBLOCK, accept4, pipe2) that allow us to simultaneously create a socket, make it nonblocking, and make it close-on-exec. This should save syscalls throughout our codebase, and avoid race-conditions if an exec() occurs after a socket is socket is created but before we can make it close-on-execute on it. 3.2. Windows-specific improvements We now use GetSystemTimeAsFileTime to implement gettimeofday. It's significantly faster and more accurate than our old ftime()-based approach. 3.3. Improvements in the solaris evport backend. The evport backend has been updated to use many of the infrastructure improvements from Libevent 2.0. Notably, it keeps track of per-fd information using the evmap infrastructure, and removes a number of linear scans over recently-added events. This last change makes it efficient to receive many more events per evport_getn() call, thereby reducing evport overhead in general. 3.4. OSX backend improvements The OSX select backend doesn't like to have more than a certain number of fds set unless an "unlimited select" option has been set. Therefore, we now set it. 3.5. Monotonic clocks on even more platforms Libevent previously used a monotonic clock for its internal timekeeping only on platforms supporting the POSIX clock_gettime() interface. Now, Libevent has support for monotonic clocks on OSX and Windows too, and a fallback implementation for systems without monotonic clocks that will at least keep time running forwards. Using monotonic timers makes Libevent more resilient to changes in the system time, as can happen in small amounts due to clock adjustments from NTP, or in large amounts due to users who move their system clocks all over the timeline in order to keep nagware from nagging them. 3.6. Faster cross-thread notification on kqueue When a thread other than the one in which the main event loop is running needs to wake the thread running the main event loop, Libevent usually writes to a socketpair in order to force the main event loop to wake up. On Linux, we've been able to use eventfd() instead. Now on BSD and OSX systems (any anywhere else that has kqueue with the EVFILT_USER extension), we can use EVFILT_USER to wake up the main thread from kqueue. This should be a tiny bit faster than the previous approach. 4. Infrastructure improvements 4.1. Faster tests I've spent some time to try to make the unit tests run faster in Libevent 2.1. Nearly all of this was a matter of searching slow tests for unreasonably long timeouts, and cutting them down to reasonably long delays, though on one or two cases I actually had to parallelize an operation or improve an algorithm. On my desktop, a full "make verify" run of Libevent 2.0.18-stable requires about 218 seconds. Libevent 2.1.1-alpha cuts this down to about 78 seconds. Faster unit tests are great, since they let programmers test their changes without losing their train of thought. 4.2. Finicky tests are now off-by-default The Tinytest unit testing framework now supports optional tests, and Libevent uses them. By default, Libevent's unit testing framework does not run tests that require a working network, and does not run tests that tend to fail on heavily loaded systems because of timing issues. To re-enable all tests, run ./test/regress using the "@all" alias. 4.3. Modernized use of autotools Our autotools-based build system has been updated to build without warnings on recent autoconf/automake versions. Libevent's autotools makefiles are no longer recursive. This allows make to use the maximum possible parallelism to do the minimally necessary amount of work. See Peter Miller's "Recursive Make Considered Harmful" at http://miller.emu.id.au/pmiller/books/rmch/ for more information here. We now use the "quiet build" option to suppress distracting messages about which commandlines are running. You can get them back with "make V=1". 4.4. Portability Libevent now uses large-file support internally on platforms where it matters. You shouldn't need to set _LARGEFILE or OFFSET_BITS or anything magic before including the Libevent headers, either, since Libevent now sets the size of ev_off_t to the size of off_t that it received at compile time, not to some (possibly different) size based on current macro definitions when your program is building. We now also use the Autoconf AC_USE_SYSTEM_EXTENSIONS mechanism to enable per-system macros needed to enable not-on-by-default features. Unlike the rest of the autoconf macros, we output these to an internal-use-only evconfig-private.h header, since their names need to survive unmangled. This lets us build correctly on more platforms, and avoid inconsistencies when some files define _GNU_SOURCE and others don't. Libevent now tries to detect OpenSSL via pkg-config. 4.5. Standards conformance Previous Libevent versions had no consistent convention for internal vs external identifiers, and used identifiers starting with the "_" character throughout the codebase. That's no good, since the C standard says that identifiers beginning with _ are reserved. I'm not aware of having any collisions with system identifiers, but it's best to fix these things before they cause trouble. We now avoid all use of the _identifiers in the Libevent source code. These changes were made *mainly* through the use of automated scripts, so there shouldn't be any mistakes, but you never know. As an exception, the names _EVENT_LOG_DEBUG, _EVENT_LOG_MSG_, _EVENT_LOG_WARN, and _EVENT_LOG_ERR are still exposed in event.h: they are now deprecated, but to support older code, they will need to stay around for a while. New code should use EVENT_LOG_DEBUG, EVENT_LOG_MSG, EVENT_LOG_WARN, and EVENT_LOG_ERR instead. 4.6. Event and callback refactoring As a simplification and optimization to Libevent's "deferred callback" logic (introduced in 2.0 to avoid callback recursion), Libevent now treats all of its deferrable callback types using the same logic it uses for active events. Now deferred events no longer cause priority inversion, no longer require special code to cancel them, and so on. Regular events and deferred callbacks now both descend from an internal light-weight event_callback supertype, and both support priorities and take part in the other anti-priority-inversion mechanisms in Libevent. To avoid starvation from callback recursion (which was the reason we introduced "deferred callbacks" in the first place) the implementation now allows an event callback to be scheduled as "active later": instead of running in the current iteration of the event loop, it runs in the next one. 5. Testing Libevent's test coverage level is more or less unchanged since before: we still have over 80% line coverage in our tests on Linux, FreeBSD, NetBSD, Windows, OSX. There are some under-tested modules, though: we need to fix those. And now we have CI: - https://travis-ci.org/libevent/libevent - https://ci.appveyor.com/project/nmathewson/libevent And code coverage: - https://coveralls.io/github/libevent/libevent Plus there is vagrant boxes if you what to test it on more OS'es then travis-ci allows, and there is a wrapper (in python) that will parse logs and provide report: - https://github.com/libevent/libevent-extras/blob/master/tools/vagrant-tests.py 6. Contributing From now we have contributing guide and checkpatch.sh. libevent-2.1.13-stable/ipv6-internal.h0000644000175000017500000000472215215775075016574 0ustar00nickmnickm/* * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Internal use only: Fake IPv6 structures and values on platforms that * do not have them */ #ifndef IPV6_INTERNAL_H_INCLUDED_ #define IPV6_INTERNAL_H_INCLUDED_ #include "event2/event-config.h" #include "evconfig-private.h" #include #ifdef EVENT__HAVE_SYS_SOCKET_H #include #endif #include "event2/util.h" #ifdef __cplusplus extern "C" { #endif /** @file ipv6-internal.h * * Replacement types and functions for platforms that don't support ipv6 * properly. */ #ifndef EVENT__HAVE_STRUCT_IN6_ADDR struct in6_addr { ev_uint8_t s6_addr[16]; }; #endif #ifndef EVENT__HAVE_SA_FAMILY_T typedef int sa_family_t; #endif #ifndef EVENT__HAVE_STRUCT_SOCKADDR_IN6 struct sockaddr_in6 { /* This will fail if we find a struct sockaddr that doesn't have * sa_family as the first element. */ sa_family_t sin6_family; ev_uint16_t sin6_port; struct in6_addr sin6_addr; }; #endif #ifndef AF_INET6 #define AF_INET6 3333 #endif #ifndef PF_INET6 #define PF_INET6 AF_INET6 #endif #ifdef __cplusplus } #endif #endif libevent-2.1.13-stable/event.c0000644000175000017500000031376115215775075015220 0ustar00nickmnickm/* * Copyright (c) 2000-2007 Niels Provos * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "event2/event-config.h" #include "evconfig-private.h" #ifdef _WIN32 #include #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN #endif #include #if !defined(_WIN32) && defined(EVENT__HAVE_SYS_TIME_H) #include #endif #include #ifdef EVENT__HAVE_SYS_SOCKET_H #include #endif #include #include #ifdef EVENT__HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #ifdef EVENT__HAVE_FCNTL_H #include #endif #include "event2/event.h" #include "event2/event_struct.h" #include "event2/event_compat.h" #include "event-internal.h" #include "defer-internal.h" #include "evthread-internal.h" #include "event2/thread.h" #include "event2/util.h" #include "log-internal.h" #include "evmap-internal.h" #include "iocp-internal.h" #include "changelist-internal.h" #define HT_NO_CACHE_HASH_VALUES #include "ht-internal.h" #include "util-internal.h" #ifdef EVENT__HAVE_WORKING_KQUEUE #include "kqueue-internal.h" #endif #ifdef EVENT__HAVE_EVENT_PORTS extern const struct eventop evportops; #endif #ifdef EVENT__HAVE_SELECT extern const struct eventop selectops; #endif #ifdef EVENT__HAVE_POLL extern const struct eventop pollops; #endif #ifdef EVENT__HAVE_EPOLL extern const struct eventop epollops; #endif #ifdef EVENT__HAVE_WORKING_KQUEUE extern const struct eventop kqops; #endif #ifdef EVENT__HAVE_DEVPOLL extern const struct eventop devpollops; #endif #ifdef _WIN32 extern const struct eventop win32ops; #endif /* Array of backends in order of preference. */ static const struct eventop *eventops[] = { #ifdef EVENT__HAVE_EVENT_PORTS &evportops, #endif #ifdef EVENT__HAVE_WORKING_KQUEUE &kqops, #endif #ifdef EVENT__HAVE_EPOLL &epollops, #endif #ifdef EVENT__HAVE_DEVPOLL &devpollops, #endif #ifdef EVENT__HAVE_POLL &pollops, #endif #ifdef EVENT__HAVE_SELECT &selectops, #endif #ifdef _WIN32 &win32ops, #endif NULL }; /* Global state; deprecated */ EVENT2_EXPORT_SYMBOL struct event_base *event_global_current_base_ = NULL; #define current_base event_global_current_base_ /* Global state */ static void *event_self_cbarg_ptr_ = NULL; /* Prototypes */ static void event_queue_insert_active(struct event_base *, struct event_callback *); static void event_queue_insert_active_later(struct event_base *, struct event_callback *); static void event_queue_insert_timeout(struct event_base *, struct event *); static void event_queue_insert_inserted(struct event_base *, struct event *); static void event_queue_remove_active(struct event_base *, struct event_callback *); static void event_queue_remove_active_later(struct event_base *, struct event_callback *); static void event_queue_remove_timeout(struct event_base *, struct event *); static void event_queue_remove_inserted(struct event_base *, struct event *); static void event_queue_make_later_events_active(struct event_base *base); static int evthread_make_base_notifiable_nolock_(struct event_base *base); static int event_del_(struct event *ev, int blocking); #ifdef USE_REINSERT_TIMEOUT /* This code seems buggy; only turn it on if we find out what the trouble is. */ static void event_queue_reinsert_timeout(struct event_base *,struct event *, int was_common, int is_common, int old_timeout_idx); #endif static int event_haveevents(struct event_base *); static int event_process_active(struct event_base *); static int timeout_next(struct event_base *, struct timeval **); static void timeout_process(struct event_base *); static inline void event_signal_closure(struct event_base *, struct event *ev); static inline void event_persist_closure(struct event_base *, struct event *ev); static int evthread_notify_base(struct event_base *base); static void insert_common_timeout_inorder(struct common_timeout_list *ctl, struct event *ev); #ifndef EVENT__DISABLE_DEBUG_MODE /* These functions implement a hashtable of which 'struct event *' structures * have been setup or added. We don't want to trust the content of the struct * event itself, since we're trying to work through cases where an event gets * clobbered or freed. Instead, we keep a hashtable indexed by the pointer. */ struct event_debug_entry { HT_ENTRY(event_debug_entry) node; const struct event *ptr; unsigned added : 1; }; static inline unsigned hash_debug_entry(const struct event_debug_entry *e) { /* We need to do this silliness to convince compilers that we * honestly mean to cast e->ptr to an integer, and discard any * part of it that doesn't fit in an unsigned. */ unsigned u = (unsigned) ((ev_uintptr_t) e->ptr); /* Our hashtable implementation is pretty sensitive to low bits, * and every struct event is over 64 bytes in size, so we can * just say >>6. */ return (u >> 6); } static inline int eq_debug_entry(const struct event_debug_entry *a, const struct event_debug_entry *b) { return a->ptr == b->ptr; } int event_debug_mode_on_ = 0; #if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE) /** * @brief debug mode variable which is set for any function/structure that needs * to be shared across threads (if thread support is enabled). * * When and if evthreads are initialized, this variable will be evaluated, * and if set to something other than zero, this means the evthread setup * functions were called out of order. * * See: "Locks and threading" in the documentation. */ int event_debug_created_threadable_ctx_ = 0; #endif /* Set if it's too late to enable event_debug_mode. */ static int event_debug_mode_too_late = 0; #ifndef EVENT__DISABLE_THREAD_SUPPORT static void *event_debug_map_lock_ = NULL; #endif static HT_HEAD(event_debug_map, event_debug_entry) global_debug_map = HT_INITIALIZER(); HT_PROTOTYPE(event_debug_map, event_debug_entry, node, hash_debug_entry, eq_debug_entry) HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, eq_debug_entry, 0.5, mm_malloc, mm_realloc, mm_free) /* record that ev is now setup (that is, ready for an add) */ static void event_debug_note_setup_(const struct event *ev) { struct event_debug_entry *dent, find; if (!event_debug_mode_on_) goto out; find.ptr = ev; EVLOCK_LOCK(event_debug_map_lock_, 0); dent = HT_FIND(event_debug_map, &global_debug_map, &find); if (dent) { dent->added = 0; } else { dent = mm_malloc(sizeof(*dent)); if (!dent) event_err(1, "Out of memory in debugging code"); dent->ptr = ev; dent->added = 0; HT_INSERT(event_debug_map, &global_debug_map, dent); } EVLOCK_UNLOCK(event_debug_map_lock_, 0); out: event_debug_mode_too_late = 1; } /* record that ev is no longer setup */ static void event_debug_note_teardown_(const struct event *ev) { struct event_debug_entry *dent, find; if (!event_debug_mode_on_) goto out; find.ptr = ev; EVLOCK_LOCK(event_debug_map_lock_, 0); dent = HT_REMOVE(event_debug_map, &global_debug_map, &find); if (dent) mm_free(dent); EVLOCK_UNLOCK(event_debug_map_lock_, 0); out: event_debug_mode_too_late = 1; } /* Macro: record that ev is now added */ static void event_debug_note_add_(const struct event *ev) { struct event_debug_entry *dent,find; if (!event_debug_mode_on_) goto out; find.ptr = ev; EVLOCK_LOCK(event_debug_map_lock_, 0); dent = HT_FIND(event_debug_map, &global_debug_map, &find); if (dent) { dent->added = 1; } else { event_errx(EVENT_ERR_ABORT_, "%s: noting an add on a non-setup event %p" " (events: 0x%x, fd: "EV_SOCK_FMT ", flags: 0x%x)", __func__, ev, ev->ev_events, EV_SOCK_ARG(ev->ev_fd), ev->ev_flags); } EVLOCK_UNLOCK(event_debug_map_lock_, 0); out: event_debug_mode_too_late = 1; } /* record that ev is no longer added */ static void event_debug_note_del_(const struct event *ev) { struct event_debug_entry *dent, find; if (!event_debug_mode_on_) goto out; find.ptr = ev; EVLOCK_LOCK(event_debug_map_lock_, 0); dent = HT_FIND(event_debug_map, &global_debug_map, &find); if (dent) { dent->added = 0; } else { event_errx(EVENT_ERR_ABORT_, "%s: noting a del on a non-setup event %p" " (events: 0x%x, fd: "EV_SOCK_FMT ", flags: 0x%x)", __func__, ev, ev->ev_events, EV_SOCK_ARG(ev->ev_fd), ev->ev_flags); } EVLOCK_UNLOCK(event_debug_map_lock_, 0); out: event_debug_mode_too_late = 1; } /* assert that ev is setup (i.e., okay to add or inspect) */ static void event_debug_assert_is_setup_(const struct event *ev) { struct event_debug_entry *dent, find; if (!event_debug_mode_on_) return; find.ptr = ev; EVLOCK_LOCK(event_debug_map_lock_, 0); dent = HT_FIND(event_debug_map, &global_debug_map, &find); if (!dent) { event_errx(EVENT_ERR_ABORT_, "%s called on a non-initialized event %p" " (events: 0x%x, fd: "EV_SOCK_FMT ", flags: 0x%x)", __func__, ev, ev->ev_events, EV_SOCK_ARG(ev->ev_fd), ev->ev_flags); } EVLOCK_UNLOCK(event_debug_map_lock_, 0); } /* assert that ev is not added (i.e., okay to tear down or set up again) */ static void event_debug_assert_not_added_(const struct event *ev) { struct event_debug_entry *dent, find; if (!event_debug_mode_on_) return; find.ptr = ev; EVLOCK_LOCK(event_debug_map_lock_, 0); dent = HT_FIND(event_debug_map, &global_debug_map, &find); if (dent && dent->added) { event_errx(EVENT_ERR_ABORT_, "%s called on an already added event %p" " (events: 0x%x, fd: "EV_SOCK_FMT", " "flags: 0x%x)", __func__, ev, ev->ev_events, EV_SOCK_ARG(ev->ev_fd), ev->ev_flags); } EVLOCK_UNLOCK(event_debug_map_lock_, 0); } static void event_debug_assert_socket_nonblocking_(evutil_socket_t fd) { if (!event_debug_mode_on_) return; if (fd < 0) return; #ifndef _WIN32 { int flags; if ((flags = fcntl(fd, F_GETFL, NULL)) >= 0) { EVUTIL_ASSERT(flags & O_NONBLOCK); } } #endif } #else static void event_debug_note_setup_(const struct event *ev) { (void)ev; } static void event_debug_note_teardown_(const struct event *ev) { (void)ev; } static void event_debug_note_add_(const struct event *ev) { (void)ev; } static void event_debug_note_del_(const struct event *ev) { (void)ev; } static void event_debug_assert_is_setup_(const struct event *ev) { (void)ev; } static void event_debug_assert_not_added_(const struct event *ev) { (void)ev; } static void event_debug_assert_socket_nonblocking_(evutil_socket_t fd) { (void)fd; } #endif #define EVENT_BASE_ASSERT_LOCKED(base) \ EVLOCK_ASSERT_LOCKED((base)->th_base_lock) /* How often (in seconds) do we check for changes in wall clock time relative * to monotonic time? Set this to -1 for 'never.' */ #define CLOCK_SYNC_INTERVAL 5 /** Set 'tp' to the current time according to 'base'. We must hold the lock * on 'base'. If there is a cached time, return it. Otherwise, use * clock_gettime or gettimeofday as appropriate to find out the right time. * Return 0 on success, -1 on failure. */ static int gettime(struct event_base *base, struct timeval *tp) { EVENT_BASE_ASSERT_LOCKED(base); if (base->tv_cache.tv_sec) { *tp = base->tv_cache; return (0); } if (evutil_gettime_monotonic_(&base->monotonic_timer, tp) == -1) { return -1; } if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL < tp->tv_sec) { struct timeval tv; evutil_gettimeofday(&tv,NULL); evutil_timersub(&tv, tp, &base->tv_clock_diff); base->last_updated_clock_diff = tp->tv_sec; } return 0; } int event_base_gettimeofday_cached(struct event_base *base, struct timeval *tv) { int r; if (!base) { base = current_base; if (!current_base) return evutil_gettimeofday(tv, NULL); } EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (base->tv_cache.tv_sec == 0) { r = evutil_gettimeofday(tv, NULL); } else { evutil_timeradd(&base->tv_cache, &base->tv_clock_diff, tv); r = 0; } EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } /** Make 'base' have no current cached time. */ static inline void clear_time_cache(struct event_base *base) { base->tv_cache.tv_sec = 0; } /** Replace the cached time in 'base' with the current time. */ static inline void update_time_cache(struct event_base *base) { base->tv_cache.tv_sec = 0; if (!(base->flags & EVENT_BASE_FLAG_NO_CACHE_TIME)) gettime(base, &base->tv_cache); } int event_base_update_cache_time(struct event_base *base) { if (!base) { base = current_base; if (!current_base) return -1; } EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (base->running_loop) update_time_cache(base); EVBASE_RELEASE_LOCK(base, th_base_lock); return 0; } static inline struct event * event_callback_to_event(struct event_callback *evcb) { EVUTIL_ASSERT((evcb->evcb_flags & EVLIST_INIT)); return EVUTIL_UPCAST(evcb, struct event, ev_evcallback); } static inline struct event_callback * event_to_event_callback(struct event *ev) { return &ev->ev_evcallback; } struct event_base * event_init(void) { struct event_base *base = event_base_new_with_config(NULL); if (base == NULL) { event_errx(1, "%s: Unable to construct event_base", __func__); return NULL; } current_base = base; return (base); } struct event_base * event_base_new(void) { struct event_base *base = NULL; struct event_config *cfg = event_config_new(); if (cfg) { base = event_base_new_with_config(cfg); event_config_free(cfg); } return base; } /** Return true iff 'method' is the name of a method that 'cfg' tells us to * avoid. */ static int event_config_is_avoided_method(const struct event_config *cfg, const char *method) { struct event_config_entry *entry; TAILQ_FOREACH(entry, &cfg->entries, next) { if (entry->avoid_method != NULL && strcmp(entry->avoid_method, method) == 0) return (1); } return (0); } /** Return true iff 'method' is disabled according to the environment. */ static int event_is_method_disabled(const char *name) { char environment[64]; int i; evutil_snprintf(environment, sizeof(environment), "EVENT_NO%s", name); for (i = 8; environment[i] != '\0'; ++i) environment[i] = EVUTIL_TOUPPER_(environment[i]); /* Note that evutil_getenv_() ignores the environment entirely if * we're setuid */ return (evutil_getenv_(environment) != NULL); } int event_base_get_features(const struct event_base *base) { return base->evsel->features; } void event_enable_debug_mode(void) { #ifndef EVENT__DISABLE_DEBUG_MODE if (event_debug_mode_on_) event_errx(1, "%s was called twice!", __func__); if (event_debug_mode_too_late) event_errx(1, "%s must be called *before* creating any events " "or event_bases",__func__); event_debug_mode_on_ = 1; HT_INIT(event_debug_map, &global_debug_map); #endif } void event_disable_debug_mode(void) { #ifndef EVENT__DISABLE_DEBUG_MODE struct event_debug_entry **ent, *victim; EVLOCK_LOCK(event_debug_map_lock_, 0); for (ent = HT_START(event_debug_map, &global_debug_map); ent; ) { victim = *ent; ent = HT_NEXT_RMV(event_debug_map, &global_debug_map, ent); mm_free(victim); } HT_CLEAR(event_debug_map, &global_debug_map); EVLOCK_UNLOCK(event_debug_map_lock_ , 0); event_debug_mode_on_ = 0; #endif } struct event_base * event_base_new_with_config(const struct event_config *cfg) { int i; struct event_base *base; int should_check_environment; #ifndef EVENT__DISABLE_DEBUG_MODE event_debug_mode_too_late = 1; #endif if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) { event_warn("%s: calloc", __func__); return NULL; } if (cfg) base->flags = cfg->flags; should_check_environment = !(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV)); { struct timeval tmp; int precise_time = cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER); int flags; if (should_check_environment && !precise_time) { precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL; if (precise_time) { base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER; } } flags = precise_time ? EV_MONOT_PRECISE : 0; evutil_configure_monotonic_time_(&base->monotonic_timer, flags); gettime(base, &tmp); } min_heap_ctor_(&base->timeheap); base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; base->th_notify_fd[0] = -1; base->th_notify_fd[1] = -1; TAILQ_INIT(&base->active_later_queue); evmap_io_initmap_(&base->io); evmap_signal_initmap_(&base->sigmap); event_changelist_init_(&base->changelist); base->evbase = NULL; if (cfg) { memcpy(&base->max_dispatch_time, &cfg->max_dispatch_interval, sizeof(struct timeval)); base->limit_callbacks_after_prio = cfg->limit_callbacks_after_prio; } else { base->max_dispatch_time.tv_sec = -1; base->limit_callbacks_after_prio = 1; } if (cfg && cfg->max_dispatch_callbacks >= 0) { base->max_dispatch_callbacks = cfg->max_dispatch_callbacks; } else { base->max_dispatch_callbacks = INT_MAX; } if (base->max_dispatch_callbacks == INT_MAX && base->max_dispatch_time.tv_sec == -1) base->limit_callbacks_after_prio = INT_MAX; for (i = 0; eventops[i] && !base->evbase; i++) { if (cfg != NULL) { /* determine if this backend should be avoided */ if (event_config_is_avoided_method(cfg, eventops[i]->name)) continue; if ((eventops[i]->features & cfg->require_features) != cfg->require_features) continue; } /* also obey the environment variables */ if (should_check_environment && event_is_method_disabled(eventops[i]->name)) continue; base->evsel = eventops[i]; base->evbase = base->evsel->init(base); } if (base->evbase == NULL) { event_warnx("%s: no event mechanism available", __func__); base->evsel = NULL; event_base_free(base); return NULL; } if (evutil_getenv_("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s", base->evsel->name); /* allocate a single active event queue */ if (event_base_priority_init(base, 1) < 0) { event_base_free(base); return NULL; } /* prepare for threading */ #if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE) event_debug_created_threadable_ctx_ = 1; #endif #ifndef EVENT__DISABLE_THREAD_SUPPORT if (EVTHREAD_LOCKING_ENABLED() && (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) { int r; EVTHREAD_ALLOC_LOCK(base->th_base_lock, 0); EVTHREAD_ALLOC_COND(base->current_event_cond); r = evthread_make_base_notifiable(base); if (r<0) { event_warnx("%s: Unable to make base notifiable.", __func__); event_base_free(base); return NULL; } } #endif #ifdef _WIN32 if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP)) event_base_start_iocp_(base, cfg->n_cpus_hint); #endif return (base); } int event_base_start_iocp_(struct event_base *base, int n_cpus) { #ifdef _WIN32 if (base->iocp) return 0; base->iocp = event_iocp_port_launch_(n_cpus); if (!base->iocp) { event_warnx("%s: Couldn't launch IOCP", __func__); return -1; } return 0; #else return -1; #endif } void event_base_stop_iocp_(struct event_base *base) { #ifdef _WIN32 int rv; if (!base->iocp) return; rv = event_iocp_shutdown_(base->iocp, -1); EVUTIL_ASSERT(rv >= 0); base->iocp = NULL; #endif } static int event_base_cancel_single_callback_(struct event_base *base, struct event_callback *evcb, int run_finalizers) { int result = 0; if (evcb->evcb_flags & EVLIST_INIT) { struct event *ev = event_callback_to_event(evcb); if (!(ev->ev_flags & EVLIST_INTERNAL)) { event_del_(ev, EVENT_DEL_EVEN_IF_FINALIZING); result = 1; } } else { EVBASE_ACQUIRE_LOCK(base, th_base_lock); event_callback_cancel_nolock_(base, evcb, 1); EVBASE_RELEASE_LOCK(base, th_base_lock); result = 1; } if (run_finalizers && (evcb->evcb_flags & EVLIST_FINALIZING)) { switch (evcb->evcb_closure) { case EV_CLOSURE_EVENT_FINALIZE: case EV_CLOSURE_EVENT_FINALIZE_FREE: { struct event *ev = event_callback_to_event(evcb); ev->ev_evcallback.evcb_cb_union.evcb_evfinalize(ev, ev->ev_arg); if (evcb->evcb_closure == EV_CLOSURE_EVENT_FINALIZE_FREE) mm_free(ev); break; } case EV_CLOSURE_CB_FINALIZE: evcb->evcb_cb_union.evcb_cbfinalize(evcb, evcb->evcb_arg); break; default: break; } } return result; } static int event_base_free_queues_(struct event_base *base, int run_finalizers) { int deleted = 0, i; for (i = 0; i < base->nactivequeues; ++i) { struct event_callback *evcb, *next; for (evcb = TAILQ_FIRST(&base->activequeues[i]); evcb; ) { next = TAILQ_NEXT(evcb, evcb_active_next); deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers); evcb = next; } } { struct event_callback *evcb; while ((evcb = TAILQ_FIRST(&base->active_later_queue))) { deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers); } } return deleted; } static void event_base_free_(struct event_base *base, int run_finalizers) { int i, n_deleted=0; struct event *ev; /* XXXX grab the lock? If there is contention when one thread frees * the base, then the contending thread will be very sad soon. */ /* event_base_free(NULL) is how to free the current_base if we * made it with event_init and forgot to hold a reference to it. */ if (base == NULL && current_base) base = current_base; /* Don't actually free NULL. */ if (base == NULL) { event_warnx("%s: no base to free", __func__); return; } /* XXX(niels) - check for internal events first */ #ifdef _WIN32 event_base_stop_iocp_(base); #endif /* threading fds if we have them */ if (base->th_notify_fd[0] != -1) { event_del(&base->th_notify); EVUTIL_CLOSESOCKET(base->th_notify_fd[0]); if (base->th_notify_fd[1] != -1) EVUTIL_CLOSESOCKET(base->th_notify_fd[1]); base->th_notify_fd[0] = -1; base->th_notify_fd[1] = -1; event_debug_unassign(&base->th_notify); } /* Delete all non-internal events. */ evmap_delete_all_(base); while ((ev = min_heap_top_(&base->timeheap)) != NULL) { event_del(ev); ++n_deleted; } for (i = 0; i < base->n_common_timeouts; ++i) { struct common_timeout_list *ctl = base->common_timeout_queues[i]; event_del(&ctl->timeout_event); /* Internal; doesn't count */ event_debug_unassign(&ctl->timeout_event); for (ev = TAILQ_FIRST(&ctl->events); ev; ) { struct event *next = TAILQ_NEXT(ev, ev_timeout_pos.ev_next_with_common_timeout); if (!(ev->ev_flags & EVLIST_INTERNAL)) { event_del(ev); ++n_deleted; } ev = next; } mm_free(ctl); } if (base->common_timeout_queues) mm_free(base->common_timeout_queues); for (;;) { /* For finalizers we can register yet another finalizer out from * finalizer, and iff finalizer will be in active_later_queue we can * add finalizer to activequeues, and we will have events in * activequeues after this function returns, which is not what we want * (we even have an assertion for this). * * A simple case is bufferevent with underlying (i.e. filters). */ int i = event_base_free_queues_(base, run_finalizers); event_debug(("%s: %d events freed", __func__, i)); if (!i) { break; } n_deleted += i; } if (n_deleted) event_debug(("%s: %d events were still set in base", __func__, n_deleted)); while (LIST_FIRST(&base->once_events)) { struct event_once *eonce = LIST_FIRST(&base->once_events); LIST_REMOVE(eonce, next_once); mm_free(eonce); } if (base->evsel != NULL && base->evsel->dealloc != NULL) base->evsel->dealloc(base); for (i = 0; i < base->nactivequeues; ++i) EVUTIL_ASSERT(TAILQ_EMPTY(&base->activequeues[i])); EVUTIL_ASSERT(min_heap_empty_(&base->timeheap)); min_heap_dtor_(&base->timeheap); mm_free(base->activequeues); evmap_io_clear_(&base->io); evmap_signal_clear_(&base->sigmap); event_changelist_freemem_(&base->changelist); EVTHREAD_FREE_LOCK(base->th_base_lock, 0); EVTHREAD_FREE_COND(base->current_event_cond); /* If we're freeing current_base, there won't be a current_base. */ if (base == current_base) current_base = NULL; mm_free(base); } void event_base_free_nofinalize(struct event_base *base) { event_base_free_(base, 0); } void event_base_free(struct event_base *base) { event_base_free_(base, 1); } /* Fake eventop; used to disable the backend temporarily inside event_reinit * so that we can call event_del() on an event without telling the backend. */ static int nil_backend_del(struct event_base *b, evutil_socket_t fd, short old, short events, void *fdinfo) { return 0; } const struct eventop nil_eventop = { "nil", NULL, /* init: unused. */ NULL, /* add: unused. */ nil_backend_del, /* del: used, so needs to be killed. */ NULL, /* dispatch: unused. */ NULL, /* dealloc: unused. */ 0, 0, 0 }; /* reinitialize the event base after a fork */ int event_reinit(struct event_base *base) { const struct eventop *evsel; int res = 0; int was_notifiable = 0; int had_signal_added = 0; EVBASE_ACQUIRE_LOCK(base, th_base_lock); evsel = base->evsel; /* check if this event mechanism requires reinit on the backend */ if (evsel->need_reinit) { /* We're going to call event_del() on our notify events (the * ones that tell about signals and wakeup events). But we * don't actually want to tell the backend to change its * state, since it might still share some resource (a kqueue, * an epoll fd) with the parent process, and we don't want to * delete the fds from _that_ backend, we temporarily stub out * the evsel with a replacement. */ base->evsel = &nil_eventop; } /* We need to re-create a new signal-notification fd and a new * thread-notification fd. Otherwise, we'll still share those with * the parent process, which would make any notification sent to them * get received by one or both of the event loops, more or less at * random. */ if (base->sig.ev_signal_added) { event_del_nolock_(&base->sig.ev_signal, EVENT_DEL_AUTOBLOCK); event_debug_unassign(&base->sig.ev_signal); memset(&base->sig.ev_signal, 0, sizeof(base->sig.ev_signal)); had_signal_added = 1; base->sig.ev_signal_added = 0; } if (base->sig.ev_signal_pair[0] != -1) EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]); if (base->sig.ev_signal_pair[1] != -1) EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]); if (base->th_notify_fn != NULL) { was_notifiable = 1; base->th_notify_fn = NULL; } if (base->th_notify_fd[0] != -1) { event_del_nolock_(&base->th_notify, EVENT_DEL_AUTOBLOCK); EVUTIL_CLOSESOCKET(base->th_notify_fd[0]); if (base->th_notify_fd[1] != -1) EVUTIL_CLOSESOCKET(base->th_notify_fd[1]); base->th_notify_fd[0] = -1; base->th_notify_fd[1] = -1; event_debug_unassign(&base->th_notify); } /* Replace the original evsel. */ base->evsel = evsel; if (evsel->need_reinit) { /* Reconstruct the backend through brute-force, so that we do * not share any structures with the parent process. For some * backends, this is necessary: epoll and kqueue, for * instance, have events associated with a kernel * structure. If didn't reinitialize, we'd share that * structure with the parent process, and any changes made by * the parent would affect our backend's behavior (and vice * versa). */ if (base->evsel->dealloc != NULL) base->evsel->dealloc(base); base->evbase = evsel->init(base); if (base->evbase == NULL) { event_errx(1, "%s: could not reinitialize event mechanism", __func__); res = -1; goto done; } /* Empty out the changelist (if any): we are starting from a * blank slate. */ event_changelist_freemem_(&base->changelist); /* Tell the event maps to re-inform the backend about all * pending events. This will make the signal notification * event get re-created if necessary. */ if (evmap_reinit_(base) < 0) res = -1; } else { res = evsig_init_(base); if (res == 0 && had_signal_added) { res = event_add_nolock_(&base->sig.ev_signal, NULL, 0); if (res == 0) base->sig.ev_signal_added = 1; } } /* If we were notifiable before, and nothing just exploded, become * notifiable again. */ if (was_notifiable && res == 0) res = evthread_make_base_notifiable_nolock_(base); done: EVBASE_RELEASE_LOCK(base, th_base_lock); return (res); } /* Get the monotonic time for this event_base' timer */ int event_gettime_monotonic(struct event_base *base, struct timeval *tv) { int rv = -1; if (base && tv) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); rv = evutil_gettime_monotonic_(&(base->monotonic_timer), tv); EVBASE_RELEASE_LOCK(base, th_base_lock); } return rv; } const char ** event_get_supported_methods(void) { static const char **methods = NULL; const struct eventop **method; const char **tmp; int i = 0, k; /* count all methods */ for (method = &eventops[0]; *method != NULL; ++method) { ++i; } /* allocate one more than we need for the NULL pointer */ tmp = mm_calloc((i + 1), sizeof(char *)); if (tmp == NULL) return (NULL); /* populate the array with the supported methods */ for (k = 0, i = 0; eventops[k] != NULL; ++k) { tmp[i++] = eventops[k]->name; } tmp[i] = NULL; if (methods != NULL) mm_free((char**)methods); methods = tmp; return (methods); } struct event_config * event_config_new(void) { struct event_config *cfg = mm_calloc(1, sizeof(*cfg)); if (cfg == NULL) return (NULL); TAILQ_INIT(&cfg->entries); cfg->max_dispatch_interval.tv_sec = -1; cfg->max_dispatch_callbacks = INT_MAX; cfg->limit_callbacks_after_prio = 1; return (cfg); } static void event_config_entry_free(struct event_config_entry *entry) { if (entry->avoid_method != NULL) mm_free((char *)entry->avoid_method); mm_free(entry); } void event_config_free(struct event_config *cfg) { struct event_config_entry *entry; while ((entry = TAILQ_FIRST(&cfg->entries)) != NULL) { TAILQ_REMOVE(&cfg->entries, entry, next); event_config_entry_free(entry); } mm_free(cfg); } int event_config_set_flag(struct event_config *cfg, int flag) { if (!cfg) return -1; cfg->flags |= flag; return 0; } int event_config_avoid_method(struct event_config *cfg, const char *method) { struct event_config_entry *entry = mm_malloc(sizeof(*entry)); if (entry == NULL) return (-1); if ((entry->avoid_method = mm_strdup(method)) == NULL) { mm_free(entry); return (-1); } TAILQ_INSERT_TAIL(&cfg->entries, entry, next); return (0); } int event_config_require_features(struct event_config *cfg, int features) { if (!cfg) return (-1); cfg->require_features = features; return (0); } int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus) { if (!cfg) return (-1); cfg->n_cpus_hint = cpus; return (0); } int event_config_set_max_dispatch_interval(struct event_config *cfg, const struct timeval *max_interval, int max_callbacks, int min_priority) { if (max_interval) memcpy(&cfg->max_dispatch_interval, max_interval, sizeof(struct timeval)); else cfg->max_dispatch_interval.tv_sec = -1; cfg->max_dispatch_callbacks = max_callbacks >= 0 ? max_callbacks : INT_MAX; if (min_priority < 0) min_priority = 0; cfg->limit_callbacks_after_prio = min_priority; return (0); } int event_priority_init(int npriorities) { return event_base_priority_init(current_base, npriorities); } int event_base_priority_init(struct event_base *base, int npriorities) { int i, r; r = -1; EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (N_ACTIVE_CALLBACKS(base) || npriorities < 1 || npriorities >= EVENT_MAX_PRIORITIES) goto err; if (npriorities == base->nactivequeues) goto ok; if (base->nactivequeues) { mm_free(base->activequeues); base->nactivequeues = 0; } /* Allocate our priority queues */ base->activequeues = (struct evcallback_list *) mm_calloc(npriorities, sizeof(struct evcallback_list)); if (base->activequeues == NULL) { event_warn("%s: calloc", __func__); goto err; } base->nactivequeues = npriorities; for (i = 0; i < base->nactivequeues; ++i) { TAILQ_INIT(&base->activequeues[i]); } ok: r = 0; err: EVBASE_RELEASE_LOCK(base, th_base_lock); return (r); } int event_base_get_npriorities(struct event_base *base) { int n; if (base == NULL) base = current_base; EVBASE_ACQUIRE_LOCK(base, th_base_lock); n = base->nactivequeues; EVBASE_RELEASE_LOCK(base, th_base_lock); return (n); } int event_base_get_num_events(struct event_base *base, unsigned int type) { int r = 0; EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (type & EVENT_BASE_COUNT_ACTIVE) r += base->event_count_active; if (type & EVENT_BASE_COUNT_VIRTUAL) r += base->virtual_event_count; if (type & EVENT_BASE_COUNT_ADDED) r += base->event_count; EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } int event_base_get_max_events(struct event_base *base, unsigned int type, int clear) { int r = 0; EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (type & EVENT_BASE_COUNT_ACTIVE) { r += base->event_count_active_max; if (clear) base->event_count_active_max = 0; } if (type & EVENT_BASE_COUNT_VIRTUAL) { r += base->virtual_event_count_max; if (clear) base->virtual_event_count_max = 0; } if (type & EVENT_BASE_COUNT_ADDED) { r += base->event_count_max; if (clear) base->event_count_max = 0; } EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } /* Returns true iff we're currently watching any events. */ static int event_haveevents(struct event_base *base) { /* Caller must hold th_base_lock */ return (base->virtual_event_count > 0 || base->event_count > 0); } /* "closure" function called when processing active signal events */ static inline void event_signal_closure(struct event_base *base, struct event *ev) { #if defined(__clang__) #elif defined(__GNUC__) #pragma GCC diagnostic push /* NOTE: it is better to avoid such code all together, by using separate * variable to break the loop in the event structure, but now this code is safe * */ #pragma GCC diagnostic ignored "-Wdangling-pointer" #endif short ncalls; int should_break; /* Allows deletes to work */ ncalls = ev->ev_ncalls; if (ncalls != 0) ev->ev_pncalls = &ncalls; EVBASE_RELEASE_LOCK(base, th_base_lock); while (ncalls) { ncalls--; ev->ev_ncalls = ncalls; if (ncalls == 0) ev->ev_pncalls = NULL; (*ev->ev_callback)(ev->ev_fd, ev->ev_res, ev->ev_arg); EVBASE_ACQUIRE_LOCK(base, th_base_lock); should_break = base->event_break; EVBASE_RELEASE_LOCK(base, th_base_lock); if (should_break) { if (ncalls != 0) ev->ev_pncalls = NULL; return; } } #if defined(__clang__) #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif } /* Common timeouts are special timeouts that are handled as queues rather than * in the minheap. This is more efficient than the minheap if we happen to * know that we're going to get several thousands of timeout events all with * the same timeout value. * * Since all our timeout handling code assumes timevals can be copied, * assigned, etc, we can't use "magic pointer" to encode these common * timeouts. Searching through a list to see if every timeout is common could * also get inefficient. Instead, we take advantage of the fact that tv_usec * is 32 bits long, but only uses 20 of those bits (since it can never be over * 999999.) We use the top bits to encode 4 bites of magic number, and 8 bits * of index into the event_base's aray of common timeouts. */ #define MICROSECONDS_MASK COMMON_TIMEOUT_MICROSECONDS_MASK #define COMMON_TIMEOUT_IDX_MASK 0x0ff00000 #define COMMON_TIMEOUT_IDX_SHIFT 20 #define COMMON_TIMEOUT_MASK 0xf0000000 #define COMMON_TIMEOUT_MAGIC 0x50000000 #define COMMON_TIMEOUT_IDX(tv) \ (((tv)->tv_usec & COMMON_TIMEOUT_IDX_MASK)>>COMMON_TIMEOUT_IDX_SHIFT) /** Return true iff if 'tv' is a common timeout in 'base' */ static inline int is_common_timeout(const struct timeval *tv, const struct event_base *base) { int idx; if ((tv->tv_usec & COMMON_TIMEOUT_MASK) != COMMON_TIMEOUT_MAGIC) return 0; idx = COMMON_TIMEOUT_IDX(tv); return idx < base->n_common_timeouts; } /* True iff tv1 and tv2 have the same common-timeout index, or if neither * one is a common timeout. */ static inline int is_same_common_timeout(const struct timeval *tv1, const struct timeval *tv2) { return (tv1->tv_usec & ~MICROSECONDS_MASK) == (tv2->tv_usec & ~MICROSECONDS_MASK); } /** Requires that 'tv' is a common timeout. Return the corresponding * common_timeout_list. */ static inline struct common_timeout_list * get_common_timeout_list(struct event_base *base, const struct timeval *tv) { return base->common_timeout_queues[COMMON_TIMEOUT_IDX(tv)]; } #if 0 static inline int common_timeout_ok(const struct timeval *tv, struct event_base *base) { const struct timeval *expect = &get_common_timeout_list(base, tv)->duration; return tv->tv_sec == expect->tv_sec && tv->tv_usec == expect->tv_usec; } #endif /* Add the timeout for the first event in given common timeout list to the * event_base's minheap. */ static void common_timeout_schedule(struct common_timeout_list *ctl, const struct timeval *now, struct event *head) { struct timeval timeout = head->ev_timeout; timeout.tv_usec &= MICROSECONDS_MASK; event_add_nolock_(&ctl->timeout_event, &timeout, 1); } /* Callback: invoked when the timeout for a common timeout queue triggers. * This means that (at least) the first event in that queue should be run, * and the timeout should be rescheduled if there are more events. */ static void common_timeout_callback(evutil_socket_t fd, short what, void *arg) { struct timeval now; struct common_timeout_list *ctl = arg; struct event_base *base = ctl->base; struct event *ev = NULL; EVBASE_ACQUIRE_LOCK(base, th_base_lock); gettime(base, &now); while (1) { ev = TAILQ_FIRST(&ctl->events); if (!ev || ev->ev_timeout.tv_sec > now.tv_sec || (ev->ev_timeout.tv_sec == now.tv_sec && (ev->ev_timeout.tv_usec&MICROSECONDS_MASK) > now.tv_usec)) break; event_del_nolock_(ev, EVENT_DEL_NOBLOCK); event_active_nolock_(ev, EV_TIMEOUT, 1); } if (ev) common_timeout_schedule(ctl, &now, ev); EVBASE_RELEASE_LOCK(base, th_base_lock); } #define MAX_COMMON_TIMEOUTS 256 const struct timeval * event_base_init_common_timeout(struct event_base *base, const struct timeval *duration) { int i; struct timeval tv; const struct timeval *result=NULL; struct common_timeout_list *new_ctl; EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (duration->tv_usec > 1000000) { memcpy(&tv, duration, sizeof(struct timeval)); if (is_common_timeout(duration, base)) tv.tv_usec &= MICROSECONDS_MASK; tv.tv_sec += tv.tv_usec / 1000000; tv.tv_usec %= 1000000; duration = &tv; } for (i = 0; i < base->n_common_timeouts; ++i) { const struct common_timeout_list *ctl = base->common_timeout_queues[i]; if (duration->tv_sec == ctl->duration.tv_sec && duration->tv_usec == (ctl->duration.tv_usec & MICROSECONDS_MASK)) { EVUTIL_ASSERT(is_common_timeout(&ctl->duration, base)); result = &ctl->duration; goto done; } } if (base->n_common_timeouts == MAX_COMMON_TIMEOUTS) { event_warnx("%s: Too many common timeouts already in use; " "we only support %d per event_base", __func__, MAX_COMMON_TIMEOUTS); goto done; } if (base->n_common_timeouts_allocated == base->n_common_timeouts) { int n = base->n_common_timeouts < 16 ? 16 : base->n_common_timeouts*2; struct common_timeout_list **newqueues = mm_realloc(base->common_timeout_queues, n*sizeof(struct common_timeout_queue *)); if (!newqueues) { event_warn("%s: realloc",__func__); goto done; } base->n_common_timeouts_allocated = n; base->common_timeout_queues = newqueues; } new_ctl = mm_calloc(1, sizeof(struct common_timeout_list)); if (!new_ctl) { event_warn("%s: calloc",__func__); goto done; } TAILQ_INIT(&new_ctl->events); new_ctl->duration.tv_sec = duration->tv_sec; new_ctl->duration.tv_usec = duration->tv_usec | COMMON_TIMEOUT_MAGIC | (base->n_common_timeouts << COMMON_TIMEOUT_IDX_SHIFT); evtimer_assign(&new_ctl->timeout_event, base, common_timeout_callback, new_ctl); new_ctl->timeout_event.ev_flags |= EVLIST_INTERNAL; event_priority_set(&new_ctl->timeout_event, 0); new_ctl->base = base; base->common_timeout_queues[base->n_common_timeouts++] = new_ctl; result = &new_ctl->duration; done: if (result) EVUTIL_ASSERT(is_common_timeout(result, base)); EVBASE_RELEASE_LOCK(base, th_base_lock); return result; } /* Closure function invoked when we're activating a persistent event. */ static inline void event_persist_closure(struct event_base *base, struct event *ev) { void (*evcb_callback)(evutil_socket_t, short, void *); // Other fields of *ev that must be stored before executing evutil_socket_t evcb_fd; short evcb_res; void *evcb_arg; /* reschedule the persistent event if we have a timeout. */ if (ev->ev_io_timeout.tv_sec || ev->ev_io_timeout.tv_usec) { /* If there was a timeout, we want it to run at an interval of * ev_io_timeout after the last time it was _scheduled_ for, * not ev_io_timeout after _now_. If it fired for another * reason, though, the timeout ought to start ticking _now_. */ struct timeval run_at, relative_to, delay, now; ev_uint32_t usec_mask = 0; EVUTIL_ASSERT(is_same_common_timeout(&ev->ev_timeout, &ev->ev_io_timeout)); gettime(base, &now); if (is_common_timeout(&ev->ev_timeout, base)) { delay = ev->ev_io_timeout; usec_mask = delay.tv_usec & ~MICROSECONDS_MASK; delay.tv_usec &= MICROSECONDS_MASK; if (ev->ev_res & EV_TIMEOUT) { relative_to = ev->ev_timeout; relative_to.tv_usec &= MICROSECONDS_MASK; } else { relative_to = now; } } else { delay = ev->ev_io_timeout; if (ev->ev_res & EV_TIMEOUT) { relative_to = ev->ev_timeout; } else { relative_to = now; } } evutil_timeradd(&relative_to, &delay, &run_at); if (evutil_timercmp(&run_at, &now, <)) { /* Looks like we missed at least one invocation due to * a clock jump, not running the event loop for a * while, really slow callbacks, or * something. Reschedule relative to now. */ evutil_timeradd(&now, &delay, &run_at); } run_at.tv_usec |= usec_mask; event_add_nolock_(ev, &run_at, 1); } // Save our callback before we release the lock evcb_callback = ev->ev_callback; evcb_fd = ev->ev_fd; evcb_res = ev->ev_res; evcb_arg = ev->ev_arg; // Release the lock EVBASE_RELEASE_LOCK(base, th_base_lock); // Execute the callback (evcb_callback)(evcb_fd, evcb_res, evcb_arg); } /* Helper for event_process_active to process all the events in a single queue, releasing the lock as we go. This function requires that the lock be held when it's invoked. Returns -1 if we get a signal or an event_break that means we should stop processing any active events now. Otherwise returns the number of non-internal event_callbacks that we processed. */ static int event_process_active_single_queue(struct event_base *base, struct evcallback_list *activeq, int max_to_process, const struct timeval *endtime) { struct event_callback *evcb; int count = 0; EVUTIL_ASSERT(activeq != NULL); for (evcb = TAILQ_FIRST(activeq); evcb; evcb = TAILQ_FIRST(activeq)) { struct event *ev=NULL; if (evcb->evcb_flags & EVLIST_INIT) { ev = event_callback_to_event(evcb); if (ev->ev_events & EV_PERSIST || ev->ev_flags & EVLIST_FINALIZING) event_queue_remove_active(base, evcb); else event_del_nolock_(ev, EVENT_DEL_NOBLOCK); event_debug(( "event_process_active: event: %p, %s%s%scall %p", ev, ev->ev_res & EV_READ ? "EV_READ " : " ", ev->ev_res & EV_WRITE ? "EV_WRITE " : " ", ev->ev_res & EV_CLOSED ? "EV_CLOSED " : " ", ev->ev_callback)); } else { event_queue_remove_active(base, evcb); event_debug(("event_process_active: event_callback %p, " "closure %d, call %p", evcb, evcb->evcb_closure, evcb->evcb_cb_union.evcb_callback)); } if (!(evcb->evcb_flags & EVLIST_INTERNAL)) ++count; base->current_event = evcb; #ifndef EVENT__DISABLE_THREAD_SUPPORT base->current_event_waiters = 0; #endif switch (evcb->evcb_closure) { case EV_CLOSURE_EVENT_SIGNAL: EVUTIL_ASSERT(ev != NULL); event_signal_closure(base, ev); break; case EV_CLOSURE_EVENT_PERSIST: EVUTIL_ASSERT(ev != NULL); event_persist_closure(base, ev); break; case EV_CLOSURE_EVENT: { void (*evcb_callback)(evutil_socket_t, short, void *); short res; EVUTIL_ASSERT(ev != NULL); evcb_callback = *ev->ev_callback; res = ev->ev_res; EVBASE_RELEASE_LOCK(base, th_base_lock); evcb_callback(ev->ev_fd, res, ev->ev_arg); } break; case EV_CLOSURE_CB_SELF: { void (*evcb_selfcb)(struct event_callback *, void *) = evcb->evcb_cb_union.evcb_selfcb; EVBASE_RELEASE_LOCK(base, th_base_lock); evcb_selfcb(evcb, evcb->evcb_arg); } break; case EV_CLOSURE_EVENT_FINALIZE: case EV_CLOSURE_EVENT_FINALIZE_FREE: { void (*evcb_evfinalize)(struct event *, void *); int evcb_closure = evcb->evcb_closure; EVUTIL_ASSERT(ev != NULL); base->current_event = NULL; evcb_evfinalize = ev->ev_evcallback.evcb_cb_union.evcb_evfinalize; EVUTIL_ASSERT((evcb->evcb_flags & EVLIST_FINALIZING)); EVBASE_RELEASE_LOCK(base, th_base_lock); event_debug_note_teardown_(ev); evcb_evfinalize(ev, ev->ev_arg); if (evcb_closure == EV_CLOSURE_EVENT_FINALIZE_FREE) mm_free(ev); } break; case EV_CLOSURE_CB_FINALIZE: { void (*evcb_cbfinalize)(struct event_callback *, void *) = evcb->evcb_cb_union.evcb_cbfinalize; base->current_event = NULL; EVUTIL_ASSERT((evcb->evcb_flags & EVLIST_FINALIZING)); EVBASE_RELEASE_LOCK(base, th_base_lock); evcb_cbfinalize(evcb, evcb->evcb_arg); } break; default: EVUTIL_ASSERT(0); } EVBASE_ACQUIRE_LOCK(base, th_base_lock); base->current_event = NULL; #ifndef EVENT__DISABLE_THREAD_SUPPORT if (base->current_event_waiters) { base->current_event_waiters = 0; EVTHREAD_COND_BROADCAST(base->current_event_cond); } #endif if (base->event_break) return -1; if (count >= max_to_process) return count; if (count && endtime) { struct timeval now; update_time_cache(base); gettime(base, &now); if (evutil_timercmp(&now, endtime, >=)) return count; } if (base->event_continue) break; } return count; } /* * Active events are stored in priority queues. Lower priorities are always * process before higher priorities. Low priority events can starve high * priority ones. */ static int event_process_active(struct event_base *base) { /* Caller must hold th_base_lock */ struct evcallback_list *activeq = NULL; int i, c = 0; const struct timeval *endtime; struct timeval tv; const int maxcb = base->max_dispatch_callbacks; const int limit_after_prio = base->limit_callbacks_after_prio; if (base->max_dispatch_time.tv_sec >= 0) { update_time_cache(base); gettime(base, &tv); evutil_timeradd(&base->max_dispatch_time, &tv, &tv); endtime = &tv; } else { endtime = NULL; } for (i = 0; i < base->nactivequeues; ++i) { if (TAILQ_FIRST(&base->activequeues[i]) != NULL) { base->event_running_priority = i; activeq = &base->activequeues[i]; if (i < limit_after_prio) c = event_process_active_single_queue(base, activeq, INT_MAX, NULL); else c = event_process_active_single_queue(base, activeq, maxcb, endtime); if (c < 0) { goto done; } else if (c > 0) break; /* Processed a real event; do not * consider lower-priority events */ /* If we get here, all of the events we processed * were internal. Continue. */ } } done: base->event_running_priority = -1; return c; } /* * Wait continuously for events. We exit only if no events are left. */ int event_dispatch(void) { return (event_loop(0)); } int event_base_dispatch(struct event_base *event_base) { return (event_base_loop(event_base, 0)); } const char * event_base_get_method(const struct event_base *base) { EVUTIL_ASSERT(base); return (base->evsel->name); } /** Callback: used to implement event_base_loopexit by telling the event_base * that it's time to exit its loop. */ static void event_loopexit_cb(evutil_socket_t fd, short what, void *arg) { struct event_base *base = arg; base->event_gotterm = 1; } int event_loopexit(const struct timeval *tv) { return (event_once(-1, EV_TIMEOUT, event_loopexit_cb, current_base, tv)); } int event_base_loopexit(struct event_base *event_base, const struct timeval *tv) { return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb, event_base, tv)); } int event_loopbreak(void) { return (event_base_loopbreak(current_base)); } int event_base_loopbreak(struct event_base *event_base) { int r = 0; if (event_base == NULL) return (-1); EVBASE_ACQUIRE_LOCK(event_base, th_base_lock); event_base->event_break = 1; if (EVBASE_NEED_NOTIFY(event_base)) { r = evthread_notify_base(event_base); } else { r = (0); } EVBASE_RELEASE_LOCK(event_base, th_base_lock); return r; } int event_base_loopcontinue(struct event_base *event_base) { int r = 0; if (event_base == NULL) return (-1); EVBASE_ACQUIRE_LOCK(event_base, th_base_lock); event_base->event_continue = 1; if (EVBASE_NEED_NOTIFY(event_base)) { r = evthread_notify_base(event_base); } else { r = (0); } EVBASE_RELEASE_LOCK(event_base, th_base_lock); return r; } int event_base_got_break(struct event_base *event_base) { int res; EVBASE_ACQUIRE_LOCK(event_base, th_base_lock); res = event_base->event_break; EVBASE_RELEASE_LOCK(event_base, th_base_lock); return res; } int event_base_got_exit(struct event_base *event_base) { int res; EVBASE_ACQUIRE_LOCK(event_base, th_base_lock); res = event_base->event_gotterm; EVBASE_RELEASE_LOCK(event_base, th_base_lock); return res; } /* not thread safe */ int event_loop(int flags) { return event_base_loop(current_base, flags); } int event_base_loop(struct event_base *base, int flags) { const struct eventop *evsel = base->evsel; struct timeval tv; struct timeval *tv_p; int res, done, retval = 0; /* Grab the lock. We will release it inside evsel.dispatch, and again * as we invoke user callbacks. */ EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (base->running_loop) { event_warnx("%s: reentrant invocation. Only one event_base_loop" " can run on each event_base at once.", __func__); EVBASE_RELEASE_LOCK(base, th_base_lock); return -1; } base->running_loop = 1; clear_time_cache(base); if (base->sig.ev_signal_added && base->sig.ev_n_signals_added) evsig_set_base_(base); done = 0; #ifndef EVENT__DISABLE_THREAD_SUPPORT base->th_owner_id = EVTHREAD_GET_ID(); #endif base->event_gotterm = base->event_break = 0; while (!done) { base->event_continue = 0; base->n_deferreds_queued = 0; /* Terminate the loop if we have been asked to */ if (base->event_gotterm) { break; } if (base->event_break) { break; } tv_p = &tv; if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) { timeout_next(base, &tv_p); } else { /* * if we have active events, we just poll new events * without waiting. */ evutil_timerclear(&tv); } /* If we have no events, we just exit */ if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) && !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) { event_debug(("%s: no events registered.", __func__)); retval = 1; goto done; } event_queue_make_later_events_active(base); clear_time_cache(base); res = evsel->dispatch(base, tv_p); if (res == -1) { event_debug(("%s: dispatch returned unsuccessfully.", __func__)); retval = -1; goto done; } update_time_cache(base); timeout_process(base); if (N_ACTIVE_CALLBACKS(base)) { int n = event_process_active(base); if ((flags & EVLOOP_ONCE) && N_ACTIVE_CALLBACKS(base) == 0 && n != 0) done = 1; } else if (flags & EVLOOP_NONBLOCK) done = 1; } event_debug(("%s: asked to terminate loop.", __func__)); done: clear_time_cache(base); base->running_loop = 0; EVBASE_RELEASE_LOCK(base, th_base_lock); return (retval); } /* One-time callback to implement event_base_once: invokes the user callback, * then deletes the allocated storage */ static void event_once_cb(evutil_socket_t fd, short events, void *arg) { struct event_once *eonce = arg; (*eonce->cb)(fd, events, eonce->arg); EVBASE_ACQUIRE_LOCK(eonce->ev.ev_base, th_base_lock); LIST_REMOVE(eonce, next_once); EVBASE_RELEASE_LOCK(eonce->ev.ev_base, th_base_lock); event_debug_unassign(&eonce->ev); mm_free(eonce); } /* not threadsafe, event scheduled once. */ int event_once(evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg, const struct timeval *tv) { return event_base_once(current_base, fd, events, callback, arg, tv); } /* Schedules an event once */ int event_base_once(struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg, const struct timeval *tv) { struct event_once *eonce; int res = 0; int activate = 0; if (!base) return (-1); /* We cannot support signals that just fire once, or persistent * events. */ if (events & (EV_SIGNAL|EV_PERSIST)) return (-1); if ((eonce = mm_calloc(1, sizeof(struct event_once))) == NULL) return (-1); eonce->cb = callback; eonce->arg = arg; if ((events & (EV_TIMEOUT|EV_SIGNAL|EV_READ|EV_WRITE|EV_CLOSED)) == EV_TIMEOUT) { evtimer_assign(&eonce->ev, base, event_once_cb, eonce); if (tv == NULL || ! evutil_timerisset(tv)) { /* If the event is going to become active immediately, * don't put it on the timeout queue. This is one * idiom for scheduling a callback, so let's make * it fast (and order-preserving). */ activate = 1; } } else if (events & (EV_READ|EV_WRITE|EV_CLOSED)) { events &= EV_READ|EV_WRITE|EV_CLOSED; event_assign(&eonce->ev, base, fd, events, event_once_cb, eonce); } else { /* Bad event combination */ mm_free(eonce); return (-1); } if (res == 0) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (activate) event_active_nolock_(&eonce->ev, EV_TIMEOUT, 1); else res = event_add_nolock_(&eonce->ev, tv, 0); if (res != 0) { mm_free(eonce); return (res); } else { LIST_INSERT_HEAD(&base->once_events, eonce, next_once); } EVBASE_RELEASE_LOCK(base, th_base_lock); } return (0); } int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg) { if (!base) base = current_base; if (arg == &event_self_cbarg_ptr_) arg = ev; if (!(events & EV_SIGNAL)) event_debug_assert_socket_nonblocking_(fd); event_debug_assert_not_added_(ev); ev->ev_base = base; ev->ev_callback = callback; ev->ev_arg = arg; ev->ev_fd = fd; ev->ev_events = events; ev->ev_res = 0; ev->ev_flags = EVLIST_INIT; ev->ev_ncalls = 0; ev->ev_pncalls = NULL; if (events & EV_SIGNAL) { if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) { event_warnx("%s: EV_SIGNAL is not compatible with " "EV_READ, EV_WRITE or EV_CLOSED", __func__); return -1; } ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL; } else { if (events & EV_PERSIST) { evutil_timerclear(&ev->ev_io_timeout); ev->ev_closure = EV_CLOSURE_EVENT_PERSIST; } else { ev->ev_closure = EV_CLOSURE_EVENT; } } min_heap_elem_init_(ev); if (base != NULL) { /* by default, we put new events into the middle priority */ ev->ev_pri = base->nactivequeues / 2; } event_debug_note_setup_(ev); return 0; } int event_base_set(struct event_base *base, struct event *ev) { /* Only innocent events may be assigned to a different base */ if (ev->ev_flags != EVLIST_INIT) return (-1); event_debug_assert_is_setup_(ev); ev->ev_base = base; ev->ev_pri = base->nactivequeues/2; return (0); } void event_set(struct event *ev, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg) { int r; r = event_assign(ev, current_base, fd, events, callback, arg); EVUTIL_ASSERT(r == 0); } void * event_self_cbarg(void) { return &event_self_cbarg_ptr_; } struct event * event_base_get_running_event(struct event_base *base) { struct event *ev = NULL; EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (EVBASE_IN_THREAD(base)) { struct event_callback *evcb = base->current_event; if (evcb->evcb_flags & EVLIST_INIT) ev = event_callback_to_event(evcb); } EVBASE_RELEASE_LOCK(base, th_base_lock); return ev; } struct event * event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg) { struct event *ev; ev = mm_malloc(sizeof(struct event)); if (ev == NULL) return (NULL); if (event_assign(ev, base, fd, events, cb, arg) < 0) { mm_free(ev); return (NULL); } return (ev); } void event_free(struct event *ev) { /* This is disabled, so that events which have been finalized be a * valid target for event_free(). That's */ // event_debug_assert_is_setup_(ev); /* make sure that this event won't be coming back to haunt us. */ event_del(ev); event_debug_note_teardown_(ev); mm_free(ev); } void event_debug_unassign(struct event *ev) { event_debug_assert_not_added_(ev); event_debug_note_teardown_(ev); ev->ev_flags &= ~EVLIST_INIT; } #define EVENT_FINALIZE_FREE_ 0x10000 static int event_finalize_nolock_(struct event_base *base, unsigned flags, struct event *ev, event_finalize_callback_fn cb) { ev_uint8_t closure = (flags & EVENT_FINALIZE_FREE_) ? EV_CLOSURE_EVENT_FINALIZE_FREE : EV_CLOSURE_EVENT_FINALIZE; event_del_nolock_(ev, EVENT_DEL_NOBLOCK); ev->ev_closure = closure; ev->ev_evcallback.evcb_cb_union.evcb_evfinalize = cb; event_active_nolock_(ev, EV_FINALIZE, 1); ev->ev_flags |= EVLIST_FINALIZING; return 0; } static int event_finalize_impl_(unsigned flags, struct event *ev, event_finalize_callback_fn cb) { int r; struct event_base *base = ev->ev_base; if (EVUTIL_FAILURE_CHECK(!base)) { event_warnx("%s: event has no event_base set.", __func__); return -1; } EVBASE_ACQUIRE_LOCK(base, th_base_lock); r = event_finalize_nolock_(base, flags, ev, cb); EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } int event_finalize(unsigned flags, struct event *ev, event_finalize_callback_fn cb) { return event_finalize_impl_(flags, ev, cb); } int event_free_finalize(unsigned flags, struct event *ev, event_finalize_callback_fn cb) { return event_finalize_impl_(flags|EVENT_FINALIZE_FREE_, ev, cb); } void event_callback_finalize_nolock_(struct event_base *base, unsigned flags, struct event_callback *evcb, void (*cb)(struct event_callback *, void *)) { struct event *ev = NULL; if (evcb->evcb_flags & EVLIST_INIT) { ev = event_callback_to_event(evcb); event_del_nolock_(ev, EVENT_DEL_NOBLOCK); } else { event_callback_cancel_nolock_(base, evcb, 0); /*XXX can this fail?*/ } evcb->evcb_closure = EV_CLOSURE_CB_FINALIZE; evcb->evcb_cb_union.evcb_cbfinalize = cb; event_callback_activate_nolock_(base, evcb); /* XXX can this really fail?*/ evcb->evcb_flags |= EVLIST_FINALIZING; } void event_callback_finalize_(struct event_base *base, unsigned flags, struct event_callback *evcb, void (*cb)(struct event_callback *, void *)) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); event_callback_finalize_nolock_(base, flags, evcb, cb); EVBASE_RELEASE_LOCK(base, th_base_lock); } /** Internal: Finalize all of the n_cbs callbacks in evcbs. The provided * callback will be invoked on *one of them*, after they have *all* been * finalized. */ int event_callback_finalize_many_(struct event_base *base, int n_cbs, struct event_callback **evcbs, void (*cb)(struct event_callback *, void *)) { int n_pending = 0, i; if (base == NULL) base = current_base; EVBASE_ACQUIRE_LOCK(base, th_base_lock); event_debug(("%s: %d events finalizing", __func__, n_cbs)); /* At most one can be currently executing; the rest we just * cancel... But we always make sure that the finalize callback * runs. */ for (i = 0; i < n_cbs; ++i) { struct event_callback *evcb = evcbs[i]; if (evcb == base->current_event) { event_callback_finalize_nolock_(base, 0, evcb, cb); ++n_pending; } else { event_callback_cancel_nolock_(base, evcb, 0); } } if (n_pending == 0) { /* Just do the first one. */ event_callback_finalize_nolock_(base, 0, evcbs[0], cb); } EVBASE_RELEASE_LOCK(base, th_base_lock); return 0; } /* * Set's the priority of an event - if an event is already scheduled * changing the priority is going to fail. */ int event_priority_set(struct event *ev, int pri) { event_debug_assert_is_setup_(ev); if (ev->ev_flags & EVLIST_ACTIVE) return (-1); if (pri < 0 || pri >= ev->ev_base->nactivequeues) return (-1); ev->ev_pri = pri; return (0); } /* * Checks if a specific event is pending or scheduled. */ int event_pending(const struct event *ev, short event, struct timeval *tv) { int flags = 0; if (EVUTIL_FAILURE_CHECK(ev->ev_base == NULL)) { event_warnx("%s: event has no event_base set.", __func__); return 0; } EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); event_debug_assert_is_setup_(ev); if (ev->ev_flags & EVLIST_INSERTED) flags |= (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)); if (ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) flags |= ev->ev_res; if (ev->ev_flags & EVLIST_TIMEOUT) flags |= EV_TIMEOUT; event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL); /* See if there is a timeout that we should report */ if (tv != NULL && (flags & event & EV_TIMEOUT)) { struct timeval tmp = ev->ev_timeout; tmp.tv_usec &= MICROSECONDS_MASK; /* correctly remamp to real time */ evutil_timeradd(&ev->ev_base->tv_clock_diff, &tmp, tv); } EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); return (flags & event); } int event_initialized(const struct event *ev) { if (!(ev->ev_flags & EVLIST_INIT)) return 0; return 1; } void event_get_assignment(const struct event *event, struct event_base **base_out, evutil_socket_t *fd_out, short *events_out, event_callback_fn *callback_out, void **arg_out) { event_debug_assert_is_setup_(event); if (base_out) *base_out = event->ev_base; if (fd_out) *fd_out = event->ev_fd; if (events_out) *events_out = event->ev_events; if (callback_out) *callback_out = event->ev_callback; if (arg_out) *arg_out = event->ev_arg; } size_t event_get_struct_event_size(void) { return sizeof(struct event); } evutil_socket_t event_get_fd(const struct event *ev) { event_debug_assert_is_setup_(ev); return ev->ev_fd; } struct event_base * event_get_base(const struct event *ev) { event_debug_assert_is_setup_(ev); return ev->ev_base; } short event_get_events(const struct event *ev) { event_debug_assert_is_setup_(ev); return ev->ev_events; } event_callback_fn event_get_callback(const struct event *ev) { event_debug_assert_is_setup_(ev); return ev->ev_callback; } void * event_get_callback_arg(const struct event *ev) { event_debug_assert_is_setup_(ev); return ev->ev_arg; } int event_get_priority(const struct event *ev) { event_debug_assert_is_setup_(ev); return ev->ev_pri; } int event_add(struct event *ev, const struct timeval *tv) { int res; if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) { event_warnx("%s: event has no event_base set.", __func__); return -1; } EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); res = event_add_nolock_(ev, tv, 0); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); return (res); } /* Helper callback: wake an event_base from another thread. This version * works by writing a byte to one end of a socketpair, so that the event_base * listening on the other end will wake up as the corresponding event * triggers */ static int evthread_notify_base_default(struct event_base *base) { char buf[1]; int r; buf[0] = (char) 0; #ifdef _WIN32 r = send(base->th_notify_fd[1], buf, 1, 0); #else r = write(base->th_notify_fd[1], buf, 1); #endif return (r < 0 && ! EVUTIL_ERR_IS_EAGAIN(errno)) ? -1 : 0; } #ifdef EVENT__HAVE_EVENTFD /* Helper callback: wake an event_base from another thread. This version * assumes that you have a working eventfd() implementation. */ static int evthread_notify_base_eventfd(struct event_base *base) { ev_uint64_t msg = 1; int r; do { r = write(base->th_notify_fd[0], (void*) &msg, sizeof(msg)); } while (r < 0 && errno == EAGAIN); return (r < 0) ? -1 : 0; } #endif /** Tell the thread currently running the event_loop for base (if any) that it * needs to stop waiting in its dispatch function (if it is) and process all * active callbacks. */ static int evthread_notify_base(struct event_base *base) { EVENT_BASE_ASSERT_LOCKED(base); if (!base->th_notify_fn) return -1; if (base->is_notify_pending) return 0; base->is_notify_pending = 1; return base->th_notify_fn(base); } /* Implementation function to remove a timeout on a currently pending event. */ int event_remove_timer_nolock_(struct event *ev) { struct event_base *base = ev->ev_base; EVENT_BASE_ASSERT_LOCKED(base); event_debug_assert_is_setup_(ev); event_debug(("event_remove_timer_nolock: event: %p", ev)); /* If it's not pending on a timeout, we don't need to do anything. */ if (ev->ev_flags & EVLIST_TIMEOUT) { event_queue_remove_timeout(base, ev); evutil_timerclear(&ev->ev_.ev_io.ev_timeout); } return (0); } int event_remove_timer(struct event *ev) { int res; if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) { event_warnx("%s: event has no event_base set.", __func__); return -1; } EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); res = event_remove_timer_nolock_(ev); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); return (res); } /* Implementation function to add an event. Works just like event_add, * except: 1) it requires that we have the lock. 2) if tv_is_absolute is set, * we treat tv as an absolute time, not as an interval to add to the current * time */ int event_add_nolock_(struct event *ev, const struct timeval *tv, int tv_is_absolute) { struct event_base *base = ev->ev_base; int res = 0; int notify = 0; EVENT_BASE_ASSERT_LOCKED(base); event_debug_assert_is_setup_(ev); event_debug(( "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p", ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback)); EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL)); if (ev->ev_flags & EVLIST_FINALIZING) { /* XXXX debug */ return (-1); } /* * prepare for timeout insertion further below, if we get a * failure on any step, we should not change any state. */ if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { if (min_heap_reserve_(&base->timeheap, 1 + min_heap_size_(&base->timeheap)) == -1) return (-1); /* ENOMEM == errno */ } /* If the main thread is currently executing a signal event's * callback, and we are not the main thread, then we want to wait * until the callback is done before we mess with the event, or else * we can race on ev_ncalls and ev_pncalls below. */ #ifndef EVENT__DISABLE_THREAD_SUPPORT if (base->current_event == event_to_event_callback(ev) && (ev->ev_events & EV_SIGNAL) && !EVBASE_IN_THREAD(base)) { ++base->current_event_waiters; EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock); } #endif if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)) res = evmap_io_add_(base, ev->ev_fd, ev); else if (ev->ev_events & EV_SIGNAL) res = evmap_signal_add_(base, (int)ev->ev_fd, ev); if (res != -1) event_queue_insert_inserted(base, ev); if (res == 1) { /* evmap says we need to notify the main thread. */ notify = 1; res = 0; } } /* * we should change the timeout state only if the previous event * addition succeeded. */ if (res != -1 && tv != NULL) { struct timeval now; int common_timeout; #ifdef USE_REINSERT_TIMEOUT int was_common; int old_timeout_idx; #endif /* * for persistent timeout events, we remember the * timeout value and re-add the event. * * If tv_is_absolute, this was already set. */ if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute) ev->ev_io_timeout = *tv; #ifndef USE_REINSERT_TIMEOUT if (ev->ev_flags & EVLIST_TIMEOUT) { event_queue_remove_timeout(base, ev); } #endif /* Check if it is active due to a timeout. Rescheduling * this timeout before the callback can be executed * removes it from the active list. */ if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) { if (ev->ev_events & EV_SIGNAL) { /* See if we are just active executing * this event in a loop */ if (ev->ev_ncalls && ev->ev_pncalls) { /* Abort loop */ *ev->ev_pncalls = 0; } } event_queue_remove_active(base, event_to_event_callback(ev)); } gettime(base, &now); common_timeout = is_common_timeout(tv, base); #ifdef USE_REINSERT_TIMEOUT was_common = is_common_timeout(&ev->ev_timeout, base); old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout); #endif if (tv_is_absolute) { ev->ev_timeout = *tv; } else if (common_timeout) { struct timeval tmp = *tv; tmp.tv_usec &= MICROSECONDS_MASK; evutil_timeradd(&now, &tmp, &ev->ev_timeout); ev->ev_timeout.tv_usec |= (tv->tv_usec & ~MICROSECONDS_MASK); } else { evutil_timeradd(&now, tv, &ev->ev_timeout); } event_debug(( "event_add: event %p, timeout in %d seconds %d useconds, call %p", ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback)); #ifdef USE_REINSERT_TIMEOUT event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx); #else event_queue_insert_timeout(base, ev); #endif if (common_timeout) { struct common_timeout_list *ctl = get_common_timeout_list(base, &ev->ev_timeout); if (ev == TAILQ_FIRST(&ctl->events)) { common_timeout_schedule(ctl, &now, ev); } } else { struct event* top = NULL; /* See if the earliest timeout is now earlier than it * was before: if so, we will need to tell the main * thread to wake up earlier than it would otherwise. * We double check the timeout of the top element to * handle time distortions due to system suspension. */ if (min_heap_elt_is_top_(ev)) notify = 1; else if ((top = min_heap_top_(&base->timeheap)) != NULL && evutil_timercmp(&top->ev_timeout, &now, <)) notify = 1; } } /* if we are not in the right thread, we need to wake up the loop */ if (res != -1 && notify && EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); event_debug_note_add_(ev); return (res); } static int event_del_(struct event *ev, int blocking) { int res; struct event_base *base = ev->ev_base; if (EVUTIL_FAILURE_CHECK(!base)) { event_warnx("%s: event has no event_base set.", __func__); return -1; } EVBASE_ACQUIRE_LOCK(base, th_base_lock); res = event_del_nolock_(ev, blocking); EVBASE_RELEASE_LOCK(base, th_base_lock); return (res); } int event_del(struct event *ev) { return event_del_(ev, EVENT_DEL_AUTOBLOCK); } int event_del_block(struct event *ev) { return event_del_(ev, EVENT_DEL_BLOCK); } int event_del_noblock(struct event *ev) { return event_del_(ev, EVENT_DEL_NOBLOCK); } /** Helper for event_del: always called with th_base_lock held. * * "blocking" must be one of the EVENT_DEL_{BLOCK, NOBLOCK, AUTOBLOCK, * EVEN_IF_FINALIZING} values. See those for more information. */ int event_del_nolock_(struct event *ev, int blocking) { struct event_base *base; int res = 0, notify = 0; event_debug(("event_del: %p (fd "EV_SOCK_FMT"), callback %p", ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_callback)); /* An event without a base has not been added */ if (ev->ev_base == NULL) return (-1); EVENT_BASE_ASSERT_LOCKED(ev->ev_base); if (blocking != EVENT_DEL_EVEN_IF_FINALIZING) { if (ev->ev_flags & EVLIST_FINALIZING) { /* XXXX Debug */ return 0; } } base = ev->ev_base; EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL)); /* See if we are just active executing this event in a loop */ if (ev->ev_events & EV_SIGNAL) { if (ev->ev_ncalls && ev->ev_pncalls) { /* Abort loop */ *ev->ev_pncalls = 0; } } if (ev->ev_flags & EVLIST_TIMEOUT) { /* NOTE: We never need to notify the main thread because of a * deleted timeout event: all that could happen if we don't is * that the dispatch loop might wake up too early. But the * point of notifying the main thread _is_ to wake up the * dispatch loop early anyway, so we wouldn't gain anything by * doing it. */ event_queue_remove_timeout(base, ev); } if (ev->ev_flags & EVLIST_ACTIVE) event_queue_remove_active(base, event_to_event_callback(ev)); else if (ev->ev_flags & EVLIST_ACTIVE_LATER) event_queue_remove_active_later(base, event_to_event_callback(ev)); if (ev->ev_flags & EVLIST_INSERTED) { event_queue_remove_inserted(base, ev); if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)) res = evmap_io_del_(base, ev->ev_fd, ev); else res = evmap_signal_del_(base, (int)ev->ev_fd, ev); if (res == 1) { /* evmap says we need to notify the main thread. */ notify = 1; res = 0; } /* If we do not have events, let's notify event base so it can * exit without waiting */ if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) notify = 1; } /* if we are not in the right thread, we need to wake up the loop */ if (res != -1 && notify && EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); event_debug_note_del_(ev); /* If the main thread is currently executing this event's callback, * and we are not the main thread, then we want to wait until the * callback is done before returning. That way, when this function * returns, it will be safe to free the user-supplied argument. */ #ifndef EVENT__DISABLE_THREAD_SUPPORT if (blocking != EVENT_DEL_NOBLOCK && base->current_event == event_to_event_callback(ev) && !EVBASE_IN_THREAD(base) && (blocking == EVENT_DEL_BLOCK || !(ev->ev_events & EV_FINALIZE))) { ++base->current_event_waiters; EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock); } #endif return (res); } void event_active(struct event *ev, int res, short ncalls) { if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) { event_warnx("%s: event has no event_base set.", __func__); return; } EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); event_debug_assert_is_setup_(ev); event_active_nolock_(ev, res, ncalls); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); } void event_active_nolock_(struct event *ev, int res, short ncalls) { struct event_base *base; event_debug(("event_active: %p (fd "EV_SOCK_FMT"), res %d, callback %p", ev, EV_SOCK_ARG(ev->ev_fd), (int)res, ev->ev_callback)); base = ev->ev_base; EVENT_BASE_ASSERT_LOCKED(base); if (ev->ev_flags & EVLIST_FINALIZING) { /* XXXX debug */ return; } switch ((ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { default: case EVLIST_ACTIVE|EVLIST_ACTIVE_LATER: EVUTIL_ASSERT(0); break; case EVLIST_ACTIVE: /* We get different kinds of events, add them together */ ev->ev_res |= res; return; case EVLIST_ACTIVE_LATER: ev->ev_res |= res; break; case 0: ev->ev_res = res; break; } if (ev->ev_pri < base->event_running_priority) base->event_continue = 1; if (ev->ev_events & EV_SIGNAL) { #ifndef EVENT__DISABLE_THREAD_SUPPORT if (base->current_event == event_to_event_callback(ev) && !EVBASE_IN_THREAD(base)) { ++base->current_event_waiters; EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock); } #endif ev->ev_ncalls = ncalls; ev->ev_pncalls = NULL; } event_callback_activate_nolock_(base, event_to_event_callback(ev)); } void event_active_later_(struct event *ev, int res) { EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); event_active_later_nolock_(ev, res); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); } void event_active_later_nolock_(struct event *ev, int res) { struct event_base *base = ev->ev_base; EVENT_BASE_ASSERT_LOCKED(base); if (ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) { /* We get different kinds of events, add them together */ ev->ev_res |= res; return; } ev->ev_res = res; event_callback_activate_later_nolock_(base, event_to_event_callback(ev)); } int event_callback_activate_(struct event_base *base, struct event_callback *evcb) { int r; EVBASE_ACQUIRE_LOCK(base, th_base_lock); r = event_callback_activate_nolock_(base, evcb); EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } int event_callback_activate_nolock_(struct event_base *base, struct event_callback *evcb) { int r = 1; if (evcb->evcb_flags & EVLIST_FINALIZING) return 0; switch (evcb->evcb_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) { default: EVUTIL_ASSERT(0); EVUTIL_FALLTHROUGH; case EVLIST_ACTIVE_LATER: event_queue_remove_active_later(base, evcb); r = 0; break; case EVLIST_ACTIVE: return 0; case 0: break; } event_queue_insert_active(base, evcb); if (EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); return r; } int event_callback_activate_later_nolock_(struct event_base *base, struct event_callback *evcb) { if (evcb->evcb_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) return 0; event_queue_insert_active_later(base, evcb); if (EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); return 1; } void event_callback_init_(struct event_base *base, struct event_callback *cb) { memset(cb, 0, sizeof(*cb)); cb->evcb_pri = base->nactivequeues - 1; } int event_callback_cancel_(struct event_base *base, struct event_callback *evcb) { int r; EVBASE_ACQUIRE_LOCK(base, th_base_lock); r = event_callback_cancel_nolock_(base, evcb, 0); EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } int event_callback_cancel_nolock_(struct event_base *base, struct event_callback *evcb, int even_if_finalizing) { if ((evcb->evcb_flags & EVLIST_FINALIZING) && !even_if_finalizing) return 0; if (evcb->evcb_flags & EVLIST_INIT) return event_del_nolock_(event_callback_to_event(evcb), even_if_finalizing ? EVENT_DEL_EVEN_IF_FINALIZING : EVENT_DEL_AUTOBLOCK); switch ((evcb->evcb_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { default: case EVLIST_ACTIVE|EVLIST_ACTIVE_LATER: EVUTIL_ASSERT(0); break; case EVLIST_ACTIVE: /* We get different kinds of events, add them together */ event_queue_remove_active(base, evcb); return 0; case EVLIST_ACTIVE_LATER: event_queue_remove_active_later(base, evcb); break; case 0: break; } return 0; } void event_deferred_cb_init_(struct event_callback *cb, ev_uint8_t priority, deferred_cb_fn fn, void *arg) { memset(cb, 0, sizeof(*cb)); cb->evcb_cb_union.evcb_selfcb = fn; cb->evcb_arg = arg; cb->evcb_pri = priority; cb->evcb_closure = EV_CLOSURE_CB_SELF; } void event_deferred_cb_set_priority_(struct event_callback *cb, ev_uint8_t priority) { cb->evcb_pri = priority; } void event_deferred_cb_cancel_(struct event_base *base, struct event_callback *cb) { if (!base) base = current_base; event_callback_cancel_(base, cb); } #define MAX_DEFERREDS_QUEUED 32 int event_deferred_cb_schedule_(struct event_base *base, struct event_callback *cb) { int r = 1; if (!base) base = current_base; EVBASE_ACQUIRE_LOCK(base, th_base_lock); if (base->n_deferreds_queued > MAX_DEFERREDS_QUEUED) { r = event_callback_activate_later_nolock_(base, cb); } else { r = event_callback_activate_nolock_(base, cb); if (r) { ++base->n_deferreds_queued; } } EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } static int timeout_next(struct event_base *base, struct timeval **tv_p) { /* Caller must hold th_base_lock */ struct timeval now; struct event *ev; struct timeval *tv = *tv_p; int res = 0; ev = min_heap_top_(&base->timeheap); if (ev == NULL) { /* if no time-based events are active wait for I/O */ *tv_p = NULL; goto out; } if (gettime(base, &now) == -1) { res = -1; goto out; } if (evutil_timercmp(&ev->ev_timeout, &now, <=)) { evutil_timerclear(tv); goto out; } evutil_timersub(&ev->ev_timeout, &now, tv); EVUTIL_ASSERT(tv->tv_sec >= 0); EVUTIL_ASSERT(tv->tv_usec >= 0); event_debug(("timeout_next: event: %p, in %d seconds, %d useconds", ev, (int)tv->tv_sec, (int)tv->tv_usec)); out: return (res); } /* Activate every event whose timeout has elapsed. */ static void timeout_process(struct event_base *base) { /* Caller must hold lock. */ struct timeval now; struct event *ev; if (min_heap_empty_(&base->timeheap)) { return; } gettime(base, &now); while ((ev = min_heap_top_(&base->timeheap))) { if (evutil_timercmp(&ev->ev_timeout, &now, >)) break; /* delete this event from the I/O queues */ event_del_nolock_(ev, EVENT_DEL_NOBLOCK); event_debug(("timeout_process: event: %p, call %p", ev, ev->ev_callback)); event_active_nolock_(ev, EV_TIMEOUT, 1); } } #ifndef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) #endif #define MAX_EVENT_COUNT(var, v) var = MAX(var, v) /* These are a fancy way to spell if (~flags & EVLIST_INTERNAL) base->event_count--/++; */ #define DECR_EVENT_COUNT(base,flags) \ ((base)->event_count -= !((flags) & EVLIST_INTERNAL)) #define INCR_EVENT_COUNT(base,flags) do { \ ((base)->event_count += !((flags) & EVLIST_INTERNAL)); \ MAX_EVENT_COUNT((base)->event_count_max, (base)->event_count); \ } while (0) static void event_queue_remove_inserted(struct event_base *base, struct event *ev) { EVENT_BASE_ASSERT_LOCKED(base); if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_INSERTED))) { event_errx(1, "%s: %p(fd "EV_SOCK_FMT") not on queue %x", __func__, ev, EV_SOCK_ARG(ev->ev_fd), EVLIST_INSERTED); return; } DECR_EVENT_COUNT(base, ev->ev_flags); ev->ev_flags &= ~EVLIST_INSERTED; } static void event_queue_remove_active(struct event_base *base, struct event_callback *evcb) { EVENT_BASE_ASSERT_LOCKED(base); if (EVUTIL_FAILURE_CHECK(!(evcb->evcb_flags & EVLIST_ACTIVE))) { event_errx(1, "%s: %p not on queue %x", __func__, evcb, EVLIST_ACTIVE); return; } DECR_EVENT_COUNT(base, evcb->evcb_flags); evcb->evcb_flags &= ~EVLIST_ACTIVE; base->event_count_active--; TAILQ_REMOVE(&base->activequeues[evcb->evcb_pri], evcb, evcb_active_next); } static void event_queue_remove_active_later(struct event_base *base, struct event_callback *evcb) { EVENT_BASE_ASSERT_LOCKED(base); if (EVUTIL_FAILURE_CHECK(!(evcb->evcb_flags & EVLIST_ACTIVE_LATER))) { event_errx(1, "%s: %p not on queue %x", __func__, evcb, EVLIST_ACTIVE_LATER); return; } DECR_EVENT_COUNT(base, evcb->evcb_flags); evcb->evcb_flags &= ~EVLIST_ACTIVE_LATER; base->event_count_active--; TAILQ_REMOVE(&base->active_later_queue, evcb, evcb_active_next); } static void event_queue_remove_timeout(struct event_base *base, struct event *ev) { EVENT_BASE_ASSERT_LOCKED(base); if (EVUTIL_FAILURE_CHECK(!(ev->ev_flags & EVLIST_TIMEOUT))) { event_errx(1, "%s: %p(fd "EV_SOCK_FMT") not on queue %x", __func__, ev, EV_SOCK_ARG(ev->ev_fd), EVLIST_TIMEOUT); return; } DECR_EVENT_COUNT(base, ev->ev_flags); ev->ev_flags &= ~EVLIST_TIMEOUT; if (is_common_timeout(&ev->ev_timeout, base)) { struct common_timeout_list *ctl = get_common_timeout_list(base, &ev->ev_timeout); TAILQ_REMOVE(&ctl->events, ev, ev_timeout_pos.ev_next_with_common_timeout); } else { min_heap_erase_(&base->timeheap, ev); } } #ifdef USE_REINSERT_TIMEOUT /* Remove and reinsert 'ev' into the timeout queue. */ static void event_queue_reinsert_timeout(struct event_base *base, struct event *ev, int was_common, int is_common, int old_timeout_idx) { struct common_timeout_list *ctl; if (!(ev->ev_flags & EVLIST_TIMEOUT)) { event_queue_insert_timeout(base, ev); return; } switch ((was_common<<1) | is_common) { case 3: /* Changing from one common timeout to another */ ctl = base->common_timeout_queues[old_timeout_idx]; TAILQ_REMOVE(&ctl->events, ev, ev_timeout_pos.ev_next_with_common_timeout); ctl = get_common_timeout_list(base, &ev->ev_timeout); insert_common_timeout_inorder(ctl, ev); break; case 2: /* Was common; is no longer common */ ctl = base->common_timeout_queues[old_timeout_idx]; TAILQ_REMOVE(&ctl->events, ev, ev_timeout_pos.ev_next_with_common_timeout); min_heap_push_(&base->timeheap, ev); break; case 1: /* Wasn't common; has become common. */ min_heap_erase_(&base->timeheap, ev); ctl = get_common_timeout_list(base, &ev->ev_timeout); insert_common_timeout_inorder(ctl, ev); break; case 0: /* was in heap; is still on heap. */ min_heap_adjust_(&base->timeheap, ev); break; default: EVUTIL_ASSERT(0); /* unreachable */ break; } } #endif /* Add 'ev' to the common timeout list in 'ev'. */ static void insert_common_timeout_inorder(struct common_timeout_list *ctl, struct event *ev) { struct event *e; /* By all logic, we should just be able to append 'ev' to the end of * ctl->events, since the timeout on each 'ev' is set to {the common * timeout} + {the time when we add the event}, and so the events * should arrive in order of their timeeouts. But just in case * there's some wacky threading issue going on, we do a search from * the end of 'ev' to find the right insertion point. */ TAILQ_FOREACH_REVERSE(e, &ctl->events, event_list, ev_timeout_pos.ev_next_with_common_timeout) { /* This timercmp is a little sneaky, since both ev and e have * magic values in tv_usec. Fortunately, they ought to have * the _same_ magic values in tv_usec. Let's assert for that. */ EVUTIL_ASSERT( is_same_common_timeout(&e->ev_timeout, &ev->ev_timeout)); if (evutil_timercmp(&ev->ev_timeout, &e->ev_timeout, >=)) { TAILQ_INSERT_AFTER(&ctl->events, e, ev, ev_timeout_pos.ev_next_with_common_timeout); return; } } TAILQ_INSERT_HEAD(&ctl->events, ev, ev_timeout_pos.ev_next_with_common_timeout); } static void event_queue_insert_inserted(struct event_base *base, struct event *ev) { EVENT_BASE_ASSERT_LOCKED(base); if (EVUTIL_FAILURE_CHECK(ev->ev_flags & EVLIST_INSERTED)) { event_errx(1, "%s: %p(fd "EV_SOCK_FMT") already inserted", __func__, ev, EV_SOCK_ARG(ev->ev_fd)); return; } INCR_EVENT_COUNT(base, ev->ev_flags); ev->ev_flags |= EVLIST_INSERTED; } static void event_queue_insert_active(struct event_base *base, struct event_callback *evcb) { EVENT_BASE_ASSERT_LOCKED(base); if (evcb->evcb_flags & EVLIST_ACTIVE) { /* Double insertion is possible for active events */ return; } INCR_EVENT_COUNT(base, evcb->evcb_flags); evcb->evcb_flags |= EVLIST_ACTIVE; base->event_count_active++; MAX_EVENT_COUNT(base->event_count_active_max, base->event_count_active); EVUTIL_ASSERT(evcb->evcb_pri < base->nactivequeues); TAILQ_INSERT_TAIL(&base->activequeues[evcb->evcb_pri], evcb, evcb_active_next); } static void event_queue_insert_active_later(struct event_base *base, struct event_callback *evcb) { EVENT_BASE_ASSERT_LOCKED(base); if (evcb->evcb_flags & (EVLIST_ACTIVE_LATER|EVLIST_ACTIVE)) { /* Double insertion is possible */ return; } INCR_EVENT_COUNT(base, evcb->evcb_flags); evcb->evcb_flags |= EVLIST_ACTIVE_LATER; base->event_count_active++; MAX_EVENT_COUNT(base->event_count_active_max, base->event_count_active); EVUTIL_ASSERT(evcb->evcb_pri < base->nactivequeues); TAILQ_INSERT_TAIL(&base->active_later_queue, evcb, evcb_active_next); } static void event_queue_insert_timeout(struct event_base *base, struct event *ev) { EVENT_BASE_ASSERT_LOCKED(base); if (EVUTIL_FAILURE_CHECK(ev->ev_flags & EVLIST_TIMEOUT)) { event_errx(1, "%s: %p(fd "EV_SOCK_FMT") already on timeout", __func__, ev, EV_SOCK_ARG(ev->ev_fd)); return; } INCR_EVENT_COUNT(base, ev->ev_flags); ev->ev_flags |= EVLIST_TIMEOUT; if (is_common_timeout(&ev->ev_timeout, base)) { struct common_timeout_list *ctl = get_common_timeout_list(base, &ev->ev_timeout); insert_common_timeout_inorder(ctl, ev); } else { min_heap_push_(&base->timeheap, ev); } } static void event_queue_make_later_events_active(struct event_base *base) { struct event_callback *evcb; EVENT_BASE_ASSERT_LOCKED(base); while ((evcb = TAILQ_FIRST(&base->active_later_queue))) { TAILQ_REMOVE(&base->active_later_queue, evcb, evcb_active_next); evcb->evcb_flags = (evcb->evcb_flags & ~EVLIST_ACTIVE_LATER) | EVLIST_ACTIVE; EVUTIL_ASSERT(evcb->evcb_pri < base->nactivequeues); TAILQ_INSERT_TAIL(&base->activequeues[evcb->evcb_pri], evcb, evcb_active_next); base->n_deferreds_queued += (evcb->evcb_closure == EV_CLOSURE_CB_SELF); } } /* Functions for debugging */ const char * event_get_version(void) { return (EVENT__VERSION); } ev_uint32_t event_get_version_number(void) { return (EVENT__NUMERIC_VERSION); } /* * No thread-safe interface needed - the information should be the same * for all threads. */ const char * event_get_method(void) { return (current_base->evsel->name); } #ifndef EVENT__DISABLE_MM_REPLACEMENT static void *(*mm_malloc_fn_)(size_t sz) = NULL; static void *(*mm_realloc_fn_)(void *p, size_t sz) = NULL; static void (*mm_free_fn_)(void *p) = NULL; void * event_mm_malloc_(size_t sz) { if (sz == 0) return NULL; if (mm_malloc_fn_) return mm_malloc_fn_(sz); else return malloc(sz); } void * event_mm_calloc_(size_t count, size_t size) { if (count == 0 || size == 0) return NULL; if (mm_malloc_fn_) { size_t sz = count * size; void *p = NULL; if (count > EV_SIZE_MAX / size) goto error; p = mm_malloc_fn_(sz); if (p) return memset(p, 0, sz); } else { void *p = calloc(count, size); #ifdef _WIN32 /* Windows calloc doesn't reliably set ENOMEM */ if (p == NULL) goto error; #endif return p; } error: errno = ENOMEM; return NULL; } char * event_mm_strdup_(const char *str) { if (!str) { errno = EINVAL; return NULL; } if (mm_malloc_fn_) { size_t ln = strlen(str); void *p = NULL; if (ln == EV_SIZE_MAX) goto error; p = mm_malloc_fn_(ln+1); if (p) return memcpy(p, str, ln+1); } else #ifdef _WIN32 return _strdup(str); #else return strdup(str); #endif error: errno = ENOMEM; return NULL; } void * event_mm_realloc_(void *ptr, size_t sz) { if (mm_realloc_fn_) return mm_realloc_fn_(ptr, sz); else return realloc(ptr, sz); } void event_mm_free_(void *ptr) { if (mm_free_fn_) mm_free_fn_(ptr); else free(ptr); } void event_set_mem_functions(void *(*malloc_fn)(size_t sz), void *(*realloc_fn)(void *ptr, size_t sz), void (*free_fn)(void *ptr)) { mm_malloc_fn_ = malloc_fn; mm_realloc_fn_ = realloc_fn; mm_free_fn_ = free_fn; } #endif #ifdef EVENT__HAVE_EVENTFD static void evthread_notify_drain_eventfd(evutil_socket_t fd, short what, void *arg) { ev_uint64_t msg; ev_ssize_t r; struct event_base *base = arg; r = read(fd, (void*) &msg, sizeof(msg)); if (r<0 && errno != EAGAIN) { event_sock_warn(fd, "Error reading from eventfd"); } EVBASE_ACQUIRE_LOCK(base, th_base_lock); base->is_notify_pending = 0; EVBASE_RELEASE_LOCK(base, th_base_lock); } #endif static void evthread_notify_drain_default(evutil_socket_t fd, short what, void *arg) { unsigned char buf[1024]; struct event_base *base = arg; #ifdef _WIN32 while (recv(fd, (char*)buf, sizeof(buf), 0) > 0) ; #else while (read(fd, (char*)buf, sizeof(buf)) > 0) ; #endif EVBASE_ACQUIRE_LOCK(base, th_base_lock); base->is_notify_pending = 0; EVBASE_RELEASE_LOCK(base, th_base_lock); } int evthread_make_base_notifiable(struct event_base *base) { int r; if (!base) return -1; EVBASE_ACQUIRE_LOCK(base, th_base_lock); r = evthread_make_base_notifiable_nolock_(base); EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } static int evthread_make_base_notifiable_nolock_(struct event_base *base) { void (*cb)(evutil_socket_t, short, void *); int (*notify)(struct event_base *); if (base->th_notify_fn != NULL) { /* The base is already notifiable: we're doing fine. */ return 0; } #if defined(EVENT__HAVE_WORKING_KQUEUE) if (base->evsel == &kqops && event_kq_add_notify_event_(base) == 0) { base->th_notify_fn = event_kq_notify_base_; /* No need to add an event here; the backend can wake * itself up just fine. */ return 0; } #endif #ifdef EVENT__HAVE_EVENTFD base->th_notify_fd[0] = evutil_eventfd_(0, EVUTIL_EFD_CLOEXEC|EVUTIL_EFD_NONBLOCK); if (base->th_notify_fd[0] >= 0) { base->th_notify_fd[1] = -1; notify = evthread_notify_base_eventfd; cb = evthread_notify_drain_eventfd; } else #endif if (evutil_make_internal_pipe_(base->th_notify_fd) == 0) { notify = evthread_notify_base_default; cb = evthread_notify_drain_default; } else { return -1; } base->th_notify_fn = notify; /* prepare an event that we can use for wakeup */ event_assign(&base->th_notify, base, base->th_notify_fd[0], EV_READ|EV_PERSIST, cb, base); /* we need to mark this as internal event */ base->th_notify.ev_flags |= EVLIST_INTERNAL; event_priority_set(&base->th_notify, 0); return event_add_nolock_(&base->th_notify, NULL, 0); } int event_base_foreach_event_nolock_(struct event_base *base, event_base_foreach_event_cb fn, void *arg) { int r, i; unsigned u; struct event *ev; /* Start out with all the EVLIST_INSERTED events. */ if ((r = evmap_foreach_event_(base, fn, arg))) return r; /* Okay, now we deal with those events that have timeouts and are in * the min-heap. */ for (u = 0; u < base->timeheap.n; ++u) { ev = base->timeheap.p[u]; if (ev->ev_flags & EVLIST_INSERTED) { /* we already processed this one */ continue; } if ((r = fn(base, ev, arg))) return r; } /* Now for the events in one of the timeout queues. * the min-heap. */ for (i = 0; i < base->n_common_timeouts; ++i) { struct common_timeout_list *ctl = base->common_timeout_queues[i]; TAILQ_FOREACH(ev, &ctl->events, ev_timeout_pos.ev_next_with_common_timeout) { if (ev->ev_flags & EVLIST_INSERTED) { /* we already processed this one */ continue; } if ((r = fn(base, ev, arg))) return r; } } /* Finally, we deal wit all the active events that we haven't touched * yet. */ for (i = 0; i < base->nactivequeues; ++i) { struct event_callback *evcb; TAILQ_FOREACH(evcb, &base->activequeues[i], evcb_active_next) { if ((evcb->evcb_flags & (EVLIST_INIT|EVLIST_INSERTED|EVLIST_TIMEOUT)) != EVLIST_INIT) { /* This isn't an event (evlist_init clear), or * we already processed it. (inserted or * timeout set */ continue; } ev = event_callback_to_event(evcb); if ((r = fn(base, ev, arg))) return r; } } return 0; } /* Helper for event_base_dump_events: called on each event in the event base; * dumps only the inserted events. */ static int dump_inserted_event_fn(const struct event_base *base, const struct event *e, void *arg) { FILE *output = arg; const char *gloss = (e->ev_events & EV_SIGNAL) ? "sig" : "fd "; if (! (e->ev_flags & (EVLIST_INSERTED|EVLIST_TIMEOUT))) return 0; fprintf(output, " %p [%s "EV_SOCK_FMT"]%s%s%s%s%s%s%s", (void*)e, gloss, EV_SOCK_ARG(e->ev_fd), (e->ev_events&EV_READ)?" Read":"", (e->ev_events&EV_WRITE)?" Write":"", (e->ev_events&EV_CLOSED)?" EOF":"", (e->ev_events&EV_SIGNAL)?" Signal":"", (e->ev_events&EV_PERSIST)?" Persist":"", (e->ev_events&EV_ET)?" ET":"", (e->ev_flags&EVLIST_INTERNAL)?" Internal":""); if (e->ev_flags & EVLIST_TIMEOUT) { struct timeval tv; tv.tv_sec = e->ev_timeout.tv_sec; tv.tv_usec = e->ev_timeout.tv_usec & MICROSECONDS_MASK; evutil_timeradd(&tv, &base->tv_clock_diff, &tv); fprintf(output, " Timeout=%ld.%06d", (long)tv.tv_sec, (int)(tv.tv_usec & MICROSECONDS_MASK)); } fputc('\n', output); return 0; } /* Helper for event_base_dump_events: called on each event in the event base; * dumps only the active events. */ static int dump_active_event_fn(const struct event_base *base, const struct event *e, void *arg) { FILE *output = arg; const char *gloss = (e->ev_events & EV_SIGNAL) ? "sig" : "fd "; if (! (e->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) return 0; fprintf(output, " %p [%s "EV_SOCK_FMT", priority=%d]%s%s%s%s%s active%s%s\n", (void*)e, gloss, EV_SOCK_ARG(e->ev_fd), e->ev_pri, (e->ev_res&EV_READ)?" Read":"", (e->ev_res&EV_WRITE)?" Write":"", (e->ev_res&EV_CLOSED)?" EOF":"", (e->ev_res&EV_SIGNAL)?" Signal":"", (e->ev_res&EV_TIMEOUT)?" Timeout":"", (e->ev_flags&EVLIST_INTERNAL)?" [Internal]":"", (e->ev_flags&EVLIST_ACTIVE_LATER)?" [NextTime]":""); return 0; } int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg) { int r; if ((!fn) || (!base)) { return -1; } EVBASE_ACQUIRE_LOCK(base, th_base_lock); r = event_base_foreach_event_nolock_(base, fn, arg); EVBASE_RELEASE_LOCK(base, th_base_lock); return r; } void event_base_dump_events(struct event_base *base, FILE *output) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); fprintf(output, "Inserted events:\n"); event_base_foreach_event_nolock_(base, dump_inserted_event_fn, output); fprintf(output, "Active events:\n"); event_base_foreach_event_nolock_(base, dump_active_event_fn, output); EVBASE_RELEASE_LOCK(base, th_base_lock); } void event_base_active_by_fd(struct event_base *base, evutil_socket_t fd, short events) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); /* Activate any non timer events */ if (!(events & EV_TIMEOUT)) { evmap_io_active_(base, fd, events & (EV_READ|EV_WRITE|EV_CLOSED)); } else { /* If we want to activate timer events, loop and activate each event with * the same fd in both the timeheap and common timeouts list */ int i; unsigned u; struct event *ev; for (u = 0; u < base->timeheap.n; ++u) { ev = base->timeheap.p[u]; if (ev->ev_fd == fd) { event_active_nolock_(ev, EV_TIMEOUT, 1); } } for (i = 0; i < base->n_common_timeouts; ++i) { struct common_timeout_list *ctl = base->common_timeout_queues[i]; TAILQ_FOREACH(ev, &ctl->events, ev_timeout_pos.ev_next_with_common_timeout) { if (ev->ev_fd == fd) { event_active_nolock_(ev, EV_TIMEOUT, 1); } } } } EVBASE_RELEASE_LOCK(base, th_base_lock); } void event_base_active_by_signal(struct event_base *base, int sig) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); evmap_signal_active_(base, sig, 1); EVBASE_RELEASE_LOCK(base, th_base_lock); } void event_base_add_virtual_(struct event_base *base) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); base->virtual_event_count++; MAX_EVENT_COUNT(base->virtual_event_count_max, base->virtual_event_count); EVBASE_RELEASE_LOCK(base, th_base_lock); } void event_base_del_virtual_(struct event_base *base) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); EVUTIL_ASSERT(base->virtual_event_count > 0); base->virtual_event_count--; if (base->virtual_event_count == 0 && EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); EVBASE_RELEASE_LOCK(base, th_base_lock); } static void event_free_debug_globals_locks(void) { #ifndef EVENT__DISABLE_THREAD_SUPPORT #ifndef EVENT__DISABLE_DEBUG_MODE if (event_debug_map_lock_ != NULL) { EVTHREAD_FREE_LOCK(event_debug_map_lock_, 0); event_debug_map_lock_ = NULL; evthreadimpl_disable_lock_debugging_(); } #endif /* EVENT__DISABLE_DEBUG_MODE */ #endif /* EVENT__DISABLE_THREAD_SUPPORT */ return; } static void event_free_debug_globals(void) { event_free_debug_globals_locks(); } static void event_free_evsig_globals(void) { evsig_free_globals_(); } static void event_free_evutil_globals(void) { evutil_free_globals_(); } static void event_free_globals(void) { event_free_debug_globals(); event_free_evsig_globals(); event_free_evutil_globals(); } void libevent_global_shutdown(void) { event_disable_debug_mode(); event_free_globals(); } #ifndef EVENT__DISABLE_THREAD_SUPPORT int event_global_setup_locks_(const int enable_locks) { #ifndef EVENT__DISABLE_DEBUG_MODE EVTHREAD_SETUP_GLOBAL_LOCK(event_debug_map_lock_, 0); #endif if (evsig_global_setup_locks_(enable_locks) < 0) return -1; if (evutil_global_setup_locks_(enable_locks) < 0) return -1; if (evutil_secure_rng_global_setup_locks_(enable_locks) < 0) return -1; return 0; } #endif void event_base_assert_ok_(struct event_base *base) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); event_base_assert_ok_nolock_(base); EVBASE_RELEASE_LOCK(base, th_base_lock); } void event_base_assert_ok_nolock_(struct event_base *base) { int i; int count; /* First do checks on the per-fd and per-signal lists */ evmap_check_integrity_(base); /* Check the heap property */ for (i = 1; i < (int)base->timeheap.n; ++i) { int parent = (i - 1) / 2; struct event *ev, *p_ev; ev = base->timeheap.p[i]; p_ev = base->timeheap.p[parent]; EVUTIL_ASSERT(ev->ev_flags & EVLIST_TIMEOUT); EVUTIL_ASSERT(evutil_timercmp(&p_ev->ev_timeout, &ev->ev_timeout, <=)); EVUTIL_ASSERT(ev->ev_timeout_pos.min_heap_idx == i); } /* Check that the common timeouts are fine */ for (i = 0; i < base->n_common_timeouts; ++i) { struct common_timeout_list *ctl = base->common_timeout_queues[i]; struct event *last=NULL, *ev; EVUTIL_ASSERT_TAILQ_OK(&ctl->events, event, ev_timeout_pos.ev_next_with_common_timeout); TAILQ_FOREACH(ev, &ctl->events, ev_timeout_pos.ev_next_with_common_timeout) { if (last) EVUTIL_ASSERT(evutil_timercmp(&last->ev_timeout, &ev->ev_timeout, <=)); EVUTIL_ASSERT(ev->ev_flags & EVLIST_TIMEOUT); EVUTIL_ASSERT(is_common_timeout(&ev->ev_timeout,base)); EVUTIL_ASSERT(COMMON_TIMEOUT_IDX(&ev->ev_timeout) == i); last = ev; } } /* Check the active queues. */ count = 0; for (i = 0; i < base->nactivequeues; ++i) { struct event_callback *evcb; EVUTIL_ASSERT_TAILQ_OK(&base->activequeues[i], event_callback, evcb_active_next); TAILQ_FOREACH(evcb, &base->activequeues[i], evcb_active_next) { EVUTIL_ASSERT((evcb->evcb_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) == EVLIST_ACTIVE); EVUTIL_ASSERT(evcb->evcb_pri == i); ++count; } } { struct event_callback *evcb; TAILQ_FOREACH(evcb, &base->active_later_queue, evcb_active_next) { EVUTIL_ASSERT((evcb->evcb_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) == EVLIST_ACTIVE_LATER); ++count; } } EVUTIL_ASSERT(count == base->event_count_active); } libevent-2.1.13-stable/epolltable-internal.h0000644000175000017500000012062015215775075020027 0ustar00nickmnickm/* * Copyright (c) 2000-2007 Niels Provos * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef EPOLLTABLE_INTERNAL_H_INCLUDED_ #define EPOLLTABLE_INTERNAL_H_INCLUDED_ /* Here are the values we're masking off to decide what operations to do. Note that since EV_READ|EV_WRITE. Note also that this table is a little sparse, since ADD+DEL is nonsensical ("xxx" in the list below.) Note also that we are shifting old_events by only 5 bits, since EV_READ is 2 and EV_WRITE is 4. The table was auto-generated with a python script, according to this pseudocode:[*0] If either the read or the write change is add+del: This is impossible; Set op==-1, events=0. Else, if either the read or the write change is add: Set events to 0. If the read change is add, or (the read change is not del, and ev_read is in old_events): Add EPOLLIN to events. If the write change is add, or (the write change is not del, and ev_write is in old_events): Add EPOLLOUT to events. If old_events is set: Set op to EPOLL_CTL_MOD [*1,*2] Else: Set op to EPOLL_CTL_ADD [*3] Else, if the read or the write change is del: Set op to EPOLL_CTL_DEL. If the read change is del: If the write change is del: Set events to EPOLLIN|EPOLLOUT Else if ev_write is in old_events: Set events to EPOLLOUT Set op to EPOLL_CTL_MOD Else Set events to EPOLLIN Else: {The write change is del.} If ev_read is in old_events: Set events to EPOLLIN Set op to EPOLL_CTL_MOD Else: Set the events to EPOLLOUT Else: There is no read or write change; set op to 0 and events to 0. The logic is a little tricky, since we had no events set on the fd before, we need to set op="ADD" and set events=the events we want to add. If we had any events set on the fd before, and we want any events to remain on the fd, we need to say op="MOD" and set events=the events we want to remain. But if we want to delete the last event, we say op="DEL" and set events=(any non-null pointer). [*0] Actually, the Python script has gotten a bit more complicated, to support EPOLLRDHUP. [*1] This MOD is only a guess. MOD might fail with ENOENT if the file was closed and a new file was opened with the same fd. If so, we'll retry with ADD. [*2] We can't replace this with a no-op even if old_events is the same as the new events: if the file was closed and reopened, we need to retry with an ADD. (We do a MOD in this case since "no change" is more common than "close and reopen", so we'll usually wind up doing 1 syscalls instead of 2.) [*3] This ADD is only a guess. There is a fun Linux kernel issue where if you have two fds for the same file (via dup) and you ADD one to an epfd, then close it, then re-create it with the same fd (via dup2 or an unlucky dup), then try to ADD it again, you'll get an EEXIST, since the struct epitem is not actually removed from the struct eventpoll until the file itself is closed. EV_CHANGE_ADD==1 EV_CHANGE_DEL==2 EV_READ ==2 EV_WRITE ==4 EV_CLOSED ==0x80 Bit 0: close change is add Bit 1: close change is del Bit 2: read change is add Bit 3: read change is del Bit 4: write change is add Bit 5: write change is del Bit 6: old events had EV_READ Bit 7: old events had EV_WRITE Bit 8: old events had EV_CLOSED */ #define EPOLL_OP_TABLE_INDEX(c) \ ( (((c)->close_change&(EV_CHANGE_ADD|EV_CHANGE_DEL))) | \ (((c)->read_change&(EV_CHANGE_ADD|EV_CHANGE_DEL)) << 2) | \ (((c)->write_change&(EV_CHANGE_ADD|EV_CHANGE_DEL)) << 4) | \ (((c)->old_events&(EV_READ|EV_WRITE)) << 5) | \ (((c)->old_events&(EV_CLOSED)) << 1) \ ) #if EV_READ != 2 || EV_WRITE != 4 || EV_CLOSED != 0x80 || EV_CHANGE_ADD != 1 || EV_CHANGE_DEL != 2 #error "Libevent's internals changed! Regenerate the op_table in epolltable-internal.h" #endif static const struct operation { int events; int op; } epoll_op_table[] = { /* old= 0, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= 0, write: 0, read: 0, close:add */ { EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write: 0, read: 0, close:del */ { EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= 0, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= 0, write: 0, read:add, close: 0 */ { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write: 0, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write: 0, read:add, close:del */ { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= 0, write: 0, read:del, close: 0 */ { EPOLLIN, EPOLL_CTL_DEL }, /* old= 0, write: 0, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write: 0, read:del, close:del */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= 0, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= 0, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= 0, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= 0, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= 0, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= 0, write:add, read: 0, close: 0 */ { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read: 0, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write:add, read: 0, close:del */ { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= 0, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read:add, close:xxx */ { 0, 255 }, /* old= 0, write:add, read:del, close: 0 */ { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read:del, close:xxx */ { 0, 255 }, /* old= 0, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= 0, write:add, read:xxx, close:add */ { 0, 255 }, /* old= 0, write:add, read:xxx, close:del */ { 0, 255 }, /* old= 0, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= 0, write:del, read: 0, close: 0 */ { EPOLLOUT, EPOLL_CTL_DEL }, /* old= 0, write:del, read: 0, close:add */ { EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write:del, read: 0, close:del */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= 0, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= 0, write:del, read:add, close: 0 */ { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write:del, read:add, close:xxx */ { 0, 255 }, /* old= 0, write:del, read:del, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, /* old= 0, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_ADD }, /* old= 0, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= 0, write:del, read:del, close:xxx */ { 0, 255 }, /* old= 0, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= 0, write:del, read:xxx, close:add */ { 0, 255 }, /* old= 0, write:del, read:xxx, close:del */ { 0, 255 }, /* old= 0, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= 0, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= 0, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= 0, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= 0, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= 0, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= 0, write:xxx, read:add, close:add */ { 0, 255 }, /* old= 0, write:xxx, read:add, close:del */ { 0, 255 }, /* old= 0, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= 0, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= 0, write:xxx, read:del, close:add */ { 0, 255 }, /* old= 0, write:xxx, read:del, close:del */ { 0, 255 }, /* old= 0, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= 0, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= 0, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= 0, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= 0, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old= r, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= r, write: 0, read: 0, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write: 0, read: 0, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= r, write: 0, read:add, close: 0 */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write: 0, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write: 0, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= r, write: 0, read:del, close: 0 */ { EPOLLIN, EPOLL_CTL_DEL }, /* old= r, write: 0, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write: 0, read:del, close:del */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= r, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= r, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= r, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= r, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= r, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= r, write:add, read: 0, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read: 0, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write:add, read: 0, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= r, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read:add, close:xxx */ { 0, 255 }, /* old= r, write:add, read:del, close: 0 */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read:del, close:xxx */ { 0, 255 }, /* old= r, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= r, write:add, read:xxx, close:add */ { 0, 255 }, /* old= r, write:add, read:xxx, close:del */ { 0, 255 }, /* old= r, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= r, write:del, read: 0, close: 0 */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read: 0, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write:del, read: 0, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= r, write:del, read:add, close: 0 */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read:add, close:xxx */ { 0, 255 }, /* old= r, write:del, read:del, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, /* old= r, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= r, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= r, write:del, read:del, close:xxx */ { 0, 255 }, /* old= r, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= r, write:del, read:xxx, close:add */ { 0, 255 }, /* old= r, write:del, read:xxx, close:del */ { 0, 255 }, /* old= r, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= r, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= r, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= r, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= r, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= r, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= r, write:xxx, read:add, close:add */ { 0, 255 }, /* old= r, write:xxx, read:add, close:del */ { 0, 255 }, /* old= r, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= r, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= r, write:xxx, read:del, close:add */ { 0, 255 }, /* old= r, write:xxx, read:del, close:del */ { 0, 255 }, /* old= r, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= r, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= r, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= r, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= r, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old= w, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= w, write: 0, read: 0, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write: 0, read: 0, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= w, write: 0, read:add, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write: 0, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= w, write: 0, read:del, close: 0 */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write: 0, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= w, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= w, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= w, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= w, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= w, write:add, read: 0, close: 0 */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read: 0, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write:add, read: 0, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= w, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read:add, close:xxx */ { 0, 255 }, /* old= w, write:add, read:del, close: 0 */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read:del, close:xxx */ { 0, 255 }, /* old= w, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= w, write:add, read:xxx, close:add */ { 0, 255 }, /* old= w, write:add, read:xxx, close:del */ { 0, 255 }, /* old= w, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= w, write:del, read: 0, close: 0 */ { EPOLLOUT, EPOLL_CTL_DEL }, /* old= w, write:del, read: 0, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write:del, read: 0, close:del */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= w, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= w, write:del, read:add, close: 0 */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= w, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= w, write:del, read:add, close:xxx */ { 0, 255 }, /* old= w, write:del, read:del, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, /* old= w, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= w, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= w, write:del, read:del, close:xxx */ { 0, 255 }, /* old= w, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= w, write:del, read:xxx, close:add */ { 0, 255 }, /* old= w, write:del, read:xxx, close:del */ { 0, 255 }, /* old= w, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= w, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= w, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= w, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= w, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= w, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= w, write:xxx, read:add, close:add */ { 0, 255 }, /* old= w, write:xxx, read:add, close:del */ { 0, 255 }, /* old= w, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= w, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= w, write:xxx, read:del, close:add */ { 0, 255 }, /* old= w, write:xxx, read:del, close:del */ { 0, 255 }, /* old= w, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= w, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= w, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= w, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= w, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old= rw, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= rw, write: 0, read: 0, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write: 0, read: 0, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= rw, write: 0, read:add, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write: 0, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write: 0, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= rw, write: 0, read:del, close: 0 */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write: 0, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write: 0, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= rw, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= rw, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= rw, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= rw, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= rw, write:add, read: 0, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write:add, read: 0, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write:add, read: 0, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= rw, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write:add, read:add, close:xxx */ { 0, 255 }, /* old= rw, write:add, read:del, close: 0 */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= rw, write:add, read:del, close:xxx */ { 0, 255 }, /* old= rw, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= rw, write:add, read:xxx, close:add */ { 0, 255 }, /* old= rw, write:add, read:xxx, close:del */ { 0, 255 }, /* old= rw, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= rw, write:del, read: 0, close: 0 */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= rw, write:del, read: 0, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write:del, read: 0, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= rw, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= rw, write:del, read:add, close: 0 */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= rw, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= rw, write:del, read:add, close:xxx */ { 0, 255 }, /* old= rw, write:del, read:del, close: 0 */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, /* old= rw, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= rw, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= rw, write:del, read:del, close:xxx */ { 0, 255 }, /* old= rw, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= rw, write:del, read:xxx, close:add */ { 0, 255 }, /* old= rw, write:del, read:xxx, close:del */ { 0, 255 }, /* old= rw, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= rw, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= rw, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= rw, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= rw, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= rw, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= rw, write:xxx, read:add, close:add */ { 0, 255 }, /* old= rw, write:xxx, read:add, close:del */ { 0, 255 }, /* old= rw, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= rw, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= rw, write:xxx, read:del, close:add */ { 0, 255 }, /* old= rw, write:xxx, read:del, close:del */ { 0, 255 }, /* old= rw, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= rw, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= rw, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= rw, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= rw, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old= c, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= c, write: 0, read: 0, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write: 0, read: 0, close:del */ { EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= c, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= c, write: 0, read:add, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write: 0, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write: 0, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= c, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= c, write: 0, read:del, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write: 0, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write: 0, read:del, close:del */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= c, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= c, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= c, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= c, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= c, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= c, write:add, read: 0, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:add, read: 0, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:add, read: 0, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= c, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= c, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= c, write:add, read:add, close:xxx */ { 0, 255 }, /* old= c, write:add, read:del, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= c, write:add, read:del, close:xxx */ { 0, 255 }, /* old= c, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= c, write:add, read:xxx, close:add */ { 0, 255 }, /* old= c, write:add, read:xxx, close:del */ { 0, 255 }, /* old= c, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= c, write:del, read: 0, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:del, read: 0, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:del, read: 0, close:del */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= c, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= c, write:del, read:add, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= c, write:del, read:add, close:xxx */ { 0, 255 }, /* old= c, write:del, read:del, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= c, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= c, write:del, read:del, close:xxx */ { 0, 255 }, /* old= c, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= c, write:del, read:xxx, close:add */ { 0, 255 }, /* old= c, write:del, read:xxx, close:del */ { 0, 255 }, /* old= c, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= c, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= c, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= c, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= c, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= c, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= c, write:xxx, read:add, close:add */ { 0, 255 }, /* old= c, write:xxx, read:add, close:del */ { 0, 255 }, /* old= c, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= c, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= c, write:xxx, read:del, close:add */ { 0, 255 }, /* old= c, write:xxx, read:del, close:del */ { 0, 255 }, /* old= c, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= c, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= c, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= c, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= c, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old= cr, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= cr, write: 0, read: 0, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write: 0, read: 0, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= cr, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= cr, write: 0, read:add, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write: 0, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write: 0, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= cr, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= cr, write: 0, read:del, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write: 0, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write: 0, read:del, close:del */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= cr, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= cr, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= cr, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= cr, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= cr, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= cr, write:add, read: 0, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:add, read: 0, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:add, read: 0, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= cr, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= cr, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= cr, write:add, read:add, close:xxx */ { 0, 255 }, /* old= cr, write:add, read:del, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= cr, write:add, read:del, close:xxx */ { 0, 255 }, /* old= cr, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= cr, write:add, read:xxx, close:add */ { 0, 255 }, /* old= cr, write:add, read:xxx, close:del */ { 0, 255 }, /* old= cr, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= cr, write:del, read: 0, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:del, read: 0, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:del, read: 0, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= cr, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= cr, write:del, read:add, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= cr, write:del, read:add, close:xxx */ { 0, 255 }, /* old= cr, write:del, read:del, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cr, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= cr, write:del, read:del, close:xxx */ { 0, 255 }, /* old= cr, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= cr, write:del, read:xxx, close:add */ { 0, 255 }, /* old= cr, write:del, read:xxx, close:del */ { 0, 255 }, /* old= cr, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= cr, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= cr, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= cr, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= cr, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= cr, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= cr, write:xxx, read:add, close:add */ { 0, 255 }, /* old= cr, write:xxx, read:add, close:del */ { 0, 255 }, /* old= cr, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= cr, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= cr, write:xxx, read:del, close:add */ { 0, 255 }, /* old= cr, write:xxx, read:del, close:del */ { 0, 255 }, /* old= cr, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= cr, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= cr, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= cr, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= cr, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old= cw, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old= cw, write: 0, read: 0, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write: 0, read: 0, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= cw, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old= cw, write: 0, read:add, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write: 0, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write: 0, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= cw, write: 0, read:add, close:xxx */ { 0, 255 }, /* old= cw, write: 0, read:del, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write: 0, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write: 0, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= cw, write: 0, read:del, close:xxx */ { 0, 255 }, /* old= cw, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old= cw, write: 0, read:xxx, close:add */ { 0, 255 }, /* old= cw, write: 0, read:xxx, close:del */ { 0, 255 }, /* old= cw, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old= cw, write:add, read: 0, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:add, read: 0, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:add, read: 0, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= cw, write:add, read: 0, close:xxx */ { 0, 255 }, /* old= cw, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old= cw, write:add, read:add, close:xxx */ { 0, 255 }, /* old= cw, write:add, read:del, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old= cw, write:add, read:del, close:xxx */ { 0, 255 }, /* old= cw, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old= cw, write:add, read:xxx, close:add */ { 0, 255 }, /* old= cw, write:add, read:xxx, close:del */ { 0, 255 }, /* old= cw, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old= cw, write:del, read: 0, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:del, read: 0, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:del, read: 0, close:del */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= cw, write:del, read: 0, close:xxx */ { 0, 255 }, /* old= cw, write:del, read:add, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old= cw, write:del, read:add, close:xxx */ { 0, 255 }, /* old= cw, write:del, read:del, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old= cw, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old= cw, write:del, read:del, close:xxx */ { 0, 255 }, /* old= cw, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old= cw, write:del, read:xxx, close:add */ { 0, 255 }, /* old= cw, write:del, read:xxx, close:del */ { 0, 255 }, /* old= cw, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old= cw, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old= cw, write:xxx, read: 0, close:add */ { 0, 255 }, /* old= cw, write:xxx, read: 0, close:del */ { 0, 255 }, /* old= cw, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old= cw, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old= cw, write:xxx, read:add, close:add */ { 0, 255 }, /* old= cw, write:xxx, read:add, close:del */ { 0, 255 }, /* old= cw, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old= cw, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old= cw, write:xxx, read:del, close:add */ { 0, 255 }, /* old= cw, write:xxx, read:del, close:del */ { 0, 255 }, /* old= cw, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old= cw, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old= cw, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old= cw, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old= cw, write:xxx, read:xxx, close:xxx */ { 0, 255 }, /* old=crw, write: 0, read: 0, close: 0 */ { 0, 0 }, /* old=crw, write: 0, read: 0, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write: 0, read: 0, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old=crw, write: 0, read: 0, close:xxx */ { 0, 255 }, /* old=crw, write: 0, read:add, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write: 0, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write: 0, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old=crw, write: 0, read:add, close:xxx */ { 0, 255 }, /* old=crw, write: 0, read:del, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write: 0, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write: 0, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old=crw, write: 0, read:del, close:xxx */ { 0, 255 }, /* old=crw, write: 0, read:xxx, close: 0 */ { 0, 255 }, /* old=crw, write: 0, read:xxx, close:add */ { 0, 255 }, /* old=crw, write: 0, read:xxx, close:del */ { 0, 255 }, /* old=crw, write: 0, read:xxx, close:xxx */ { 0, 255 }, /* old=crw, write:add, read: 0, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:add, read: 0, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:add, read: 0, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old=crw, write:add, read: 0, close:xxx */ { 0, 255 }, /* old=crw, write:add, read:add, close: 0 */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:add, read:add, close:add */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:add, read:add, close:del */ { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, /* old=crw, write:add, read:add, close:xxx */ { 0, 255 }, /* old=crw, write:add, read:del, close: 0 */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:add, read:del, close:add */ { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:add, read:del, close:del */ { EPOLLOUT, EPOLL_CTL_MOD }, /* old=crw, write:add, read:del, close:xxx */ { 0, 255 }, /* old=crw, write:add, read:xxx, close: 0 */ { 0, 255 }, /* old=crw, write:add, read:xxx, close:add */ { 0, 255 }, /* old=crw, write:add, read:xxx, close:del */ { 0, 255 }, /* old=crw, write:add, read:xxx, close:xxx */ { 0, 255 }, /* old=crw, write:del, read: 0, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:del, read: 0, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:del, read: 0, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old=crw, write:del, read: 0, close:xxx */ { 0, 255 }, /* old=crw, write:del, read:add, close: 0 */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:del, read:add, close:add */ { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:del, read:add, close:del */ { EPOLLIN, EPOLL_CTL_MOD }, /* old=crw, write:del, read:add, close:xxx */ { 0, 255 }, /* old=crw, write:del, read:del, close: 0 */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:del, read:del, close:add */ { EPOLLRDHUP, EPOLL_CTL_MOD }, /* old=crw, write:del, read:del, close:del */ { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, /* old=crw, write:del, read:del, close:xxx */ { 0, 255 }, /* old=crw, write:del, read:xxx, close: 0 */ { 0, 255 }, /* old=crw, write:del, read:xxx, close:add */ { 0, 255 }, /* old=crw, write:del, read:xxx, close:del */ { 0, 255 }, /* old=crw, write:del, read:xxx, close:xxx */ { 0, 255 }, /* old=crw, write:xxx, read: 0, close: 0 */ { 0, 255 }, /* old=crw, write:xxx, read: 0, close:add */ { 0, 255 }, /* old=crw, write:xxx, read: 0, close:del */ { 0, 255 }, /* old=crw, write:xxx, read: 0, close:xxx */ { 0, 255 }, /* old=crw, write:xxx, read:add, close: 0 */ { 0, 255 }, /* old=crw, write:xxx, read:add, close:add */ { 0, 255 }, /* old=crw, write:xxx, read:add, close:del */ { 0, 255 }, /* old=crw, write:xxx, read:add, close:xxx */ { 0, 255 }, /* old=crw, write:xxx, read:del, close: 0 */ { 0, 255 }, /* old=crw, write:xxx, read:del, close:add */ { 0, 255 }, /* old=crw, write:xxx, read:del, close:del */ { 0, 255 }, /* old=crw, write:xxx, read:del, close:xxx */ { 0, 255 }, /* old=crw, write:xxx, read:xxx, close: 0 */ { 0, 255 }, /* old=crw, write:xxx, read:xxx, close:add */ { 0, 255 }, /* old=crw, write:xxx, read:xxx, close:del */ { 0, 255 }, /* old=crw, write:xxx, read:xxx, close:xxx */ { 0, 255 }, }; #endif libevent-2.1.13-stable/http-internal.h0000644000175000017500000001462515221015211016642 0ustar00nickmnickm/* * Copyright 2001-2007 Niels Provos * Copyright 2007-2012 Niels Provos and Nick Mathewson * * This header file contains definitions for dealing with HTTP requests * that are internal to libevent. As user of the library, you should not * need to know about these. */ #ifndef HTTP_INTERNAL_H_INCLUDED_ #define HTTP_INTERNAL_H_INCLUDED_ #include "event2/event_struct.h" #include "util-internal.h" #include "defer-internal.h" #define HTTP_CONNECT_TIMEOUT 45 #define HTTP_WRITE_TIMEOUT 50 #define HTTP_READ_TIMEOUT 50 enum message_read_status { ALL_DATA_READ = 1, MORE_DATA_EXPECTED = 0, DATA_CORRUPTED = -1, REQUEST_CANCELED = -2, DATA_TOO_LONG = -3 }; struct evbuffer; struct addrinfo; struct evhttp_request; /* Indicates an unknown request method. */ #define EVHTTP_REQ_UNKNOWN_ (1<<15) enum evhttp_connection_state { EVCON_DISCONNECTED, /**< not currently connected not trying either*/ EVCON_CONNECTING, /**< tries to currently connect */ EVCON_IDLE, /**< connection is established */ EVCON_READING_FIRSTLINE,/**< reading Request-Line (incoming conn) or **< Status-Line (outgoing conn) */ EVCON_READING_HEADERS, /**< reading request/response headers */ EVCON_READING_BODY, /**< reading request/response body */ EVCON_READING_TRAILER, /**< reading request/response chunked trailer */ EVCON_WRITING /**< writing request/response headers/body */ }; struct event_base; /* A client or server connection. */ struct evhttp_connection { /* we use this tailq only if this connection was created for an http * server */ TAILQ_ENTRY(evhttp_connection) next; evutil_socket_t fd; struct bufferevent *bufev; struct event retry_ev; /* for retrying connects */ char *bind_address; /* address to use for binding the src */ ev_uint16_t bind_port; /* local port for binding the src */ char *address; /* address to connect to */ ev_uint16_t port; size_t max_headers_size; ev_uint64_t max_body_size; int flags; #define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */ #define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */ #define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */ /* set when we want to auto free the connection */ #define EVHTTP_CON_AUTOFREE EVHTTP_CON_PUBLIC_FLAGS_END /* Installed when attempt to read HTTP error after write failed, see * EVHTTP_CON_READ_ON_WRITE_ERROR */ #define EVHTTP_CON_READING_ERROR (EVHTTP_CON_AUTOFREE << 1) struct timeval timeout; /* timeout for events */ int retry_cnt; /* retry count */ int retry_max; /* maximum number of retries */ struct timeval initial_retry_timeout; /* Timeout for low long to wait * after first failing attempt * before retry */ enum evhttp_connection_state state; /* for server connections, the http server they are connected with */ struct evhttp *http_server; TAILQ_HEAD(evcon_requestq, evhttp_request) requests; void (*cb)(struct evhttp_connection *, void *); void *cb_arg; void (*closecb)(struct evhttp_connection *, void *); void *closecb_arg; struct event_callback read_more_deferred_cb; struct event_base *base; struct evdns_base *dns_base; int ai_family; }; /* A callback for an http server */ struct evhttp_cb { TAILQ_ENTRY(evhttp_cb) next; char *what; void (*cb)(struct evhttp_request *req, void *); void *cbarg; }; /* both the http server as well as the rpc system need to queue connections */ TAILQ_HEAD(evconq, evhttp_connection); /* each bound socket is stored in one of these */ struct evhttp_bound_socket { TAILQ_ENTRY(evhttp_bound_socket) next; struct evconnlistener *listener; }; /* server alias list item. */ struct evhttp_server_alias { TAILQ_ENTRY(evhttp_server_alias) next; char *alias; /* the server alias. */ }; struct evhttp { /* Next vhost, if this is a vhost. */ TAILQ_ENTRY(evhttp) next_vhost; /* All listeners for this host */ TAILQ_HEAD(boundq, evhttp_bound_socket) sockets; TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; /* All live connections on this host. */ struct evconq connections; TAILQ_HEAD(vhostsq, evhttp) virtualhosts; TAILQ_HEAD(aliasq, evhttp_server_alias) aliases; /* NULL if this server is not a vhost */ char *vhost_pattern; struct timeval timeout; size_t default_max_headers_size; ev_uint64_t default_max_body_size; int flags; const char *default_content_type; /* Bitmask of all HTTP methods that we accept and pass to user * callbacks. */ ev_uint16_t allowed_methods; /* Fallback callback if all the other callbacks for this connection don't match. */ void (*gencb)(struct evhttp_request *req, void *); void *gencbarg; struct bufferevent* (*bevcb)(struct event_base *, void *); void *bevcbarg; struct event_base *base; }; /* XXX most of these functions could be static. */ /* resets the connection; can be reused for more requests */ void evhttp_connection_reset_(struct evhttp_connection *); /* connects if necessary */ int evhttp_connection_connect_(struct evhttp_connection *); enum evhttp_request_error; /* notifies the current request that it failed; resets connection */ EVENT2_EXPORT_SYMBOL void evhttp_connection_fail_(struct evhttp_connection *, enum evhttp_request_error error); enum evhttp_transfer_encoding_header_status { /* This Transfer-Encoding line was invalid. * (We consider all "chunked" encodings invalid except those at the end * of the sequence.) */ TE_INVALID, /* This Transfer-Encoding line ended with the "chunked" encoding. */ TE_ENDS_IN_CHUNKED, /* This Transfer-Encoding line was valid and did not contain "chunked" */ TE_NO_CHUNKED, }; EVENT2_EXPORT_SYMBOL int evhttp_str_is_chunked_(const char *value, const char *eos); EVENT2_EXPORT_SYMBOL enum evhttp_transfer_encoding_header_status evhttp_check_transfer_encoding_(const char *value); enum message_read_status; EVENT2_EXPORT_SYMBOL enum message_read_status evhttp_parse_firstline_(struct evhttp_request *, struct evbuffer*); EVENT2_EXPORT_SYMBOL enum message_read_status evhttp_parse_headers_(struct evhttp_request *, struct evbuffer*); void evhttp_start_read_(struct evhttp_connection *); void evhttp_start_write_(struct evhttp_connection *); /* response sending HTML the data in the buffer */ void evhttp_response_code_(struct evhttp_request *, int, const char *); void evhttp_send_page_(struct evhttp_request *, struct evbuffer *); EVENT2_EXPORT_SYMBOL int evhttp_decode_uri_internal(const char *uri, size_t length, char *ret, int decode_plus); #endif /* HTTP_INTERNAL_H_INCLUDED_ */ libevent-2.1.13-stable/evdns.c0000644000175000017500000040277315221012604015173 0ustar00nickmnickm/* Copyright 2006-2007 Niels Provos * Copyright 2007-2012 Nick Mathewson and Niels Provos * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Based on software by Adam Langly. Adam's original message: * * Async DNS Library * Adam Langley * http://www.imperialviolet.org/eventdns.html * Public Domain code * * This software is Public Domain. To view a copy of the public domain dedication, * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. * * I ask and expect, but do not require, that all derivative works contain an * attribution similar to: * Parts developed by Adam Langley * * You may wish to replace the word "Parts" with something else depending on * the amount of original code. * * (Derivative works does not include programs which link against, run or include * the source verbatim in their source distributions) * * Version: 0.1b */ #include "event2/event-config.h" #include "evconfig-private.h" #include #ifndef _FORTIFY_SOURCE #define _FORTIFY_SOURCE 3 #endif #include #include #ifdef EVENT__HAVE_SYS_TIME_H #include #endif #ifdef EVENT__HAVE_STDINT_H #include #endif #include #include #include #ifdef EVENT__HAVE_UNISTD_H #include #endif #include #include #include #include #ifdef _WIN32 #include #include #include #ifndef _WIN32_IE #define _WIN32_IE 0x400 #endif #include #endif #include "event2/dns.h" #include "event2/dns_struct.h" #include "event2/dns_compat.h" #include "event2/util.h" #include "event2/event.h" #include "event2/event_struct.h" #include "event2/thread.h" #include "defer-internal.h" #include "log-internal.h" #include "mm-internal.h" #include "strlcpy-internal.h" #include "ipv6-internal.h" #include "util-internal.h" #include "evthread-internal.h" #ifdef _WIN32 #include #include #include #include #include #else #include #include #include #endif #ifdef EVENT__HAVE_NETINET_IN6_H #include #endif #define EVDNS_LOG_DEBUG EVENT_LOG_DEBUG #define EVDNS_LOG_WARN EVENT_LOG_WARN #define EVDNS_LOG_MSG EVENT_LOG_MSG #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 255 #endif #include #undef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #define ASSERT_VALID_REQUEST(req) \ EVUTIL_ASSERT((req)->handle && (req)->handle->current_req == (req)) #define u64 ev_uint64_t #define u32 ev_uint32_t #define u16 ev_uint16_t #define u8 ev_uint8_t /* maximum number of addresses from a single packet */ /* that we bother recording */ #define MAX_V4_ADDRS 32 #define MAX_V6_ADDRS 32 #define TYPE_A EVDNS_TYPE_A #define TYPE_CNAME 5 #define TYPE_PTR EVDNS_TYPE_PTR #define TYPE_SOA EVDNS_TYPE_SOA #define TYPE_AAAA EVDNS_TYPE_AAAA #define CLASS_INET EVDNS_CLASS_INET /* Persistent handle. We keep this separate from 'struct request' since we * need some object to last for as long as an evdns_request is outstanding so * that it can be canceled, whereas a search request can lead to multiple * 'struct request' instances being created over its lifetime. */ struct evdns_request { struct request *current_req; struct evdns_base *base; int pending_cb; /* Waiting for its callback to be invoked; not * owned by event base any more. */ /* elements used by the searching code */ int search_index; struct search_state *search_state; char *search_origname; /* needs to be free()ed */ int search_flags; }; struct request { u8 *request; /* the dns packet data */ u8 request_type; /* TYPE_PTR or TYPE_A or TYPE_AAAA */ unsigned int request_len; int reissue_count; int tx_count; /* the number of times that this packet has been sent */ void *user_pointer; /* the pointer given to us for this request */ evdns_callback_type user_callback; struct nameserver *ns; /* the server which we last sent it */ /* these objects are kept in a circular list */ /* XXX We could turn this into a CIRCLEQ. */ struct request *next, *prev; struct event timeout_event; u16 trans_id; /* the transaction id */ unsigned request_appended :1; /* true if the request pointer is data which follows this struct */ unsigned transmit_me :1; /* needs to be transmitted */ /* XXXX This is a horrible hack. */ char **put_cname_in_ptr; /* store the cname here if we get one. */ struct evdns_base *base; struct evdns_request *handle; }; struct reply { unsigned int type; unsigned int have_answer : 1; union { struct { u32 addrcount; u32 addresses[MAX_V4_ADDRS]; } a; struct { u32 addrcount; struct in6_addr addresses[MAX_V6_ADDRS]; } aaaa; struct { char name[HOST_NAME_MAX]; } ptr; } data; }; struct nameserver { evutil_socket_t socket; /* a connected UDP socket */ struct sockaddr_storage address; ev_socklen_t addrlen; int failed_times; /* number of times which we have given this server a chance */ int timedout; /* number of times in a row a request has timed out */ struct event event; /* these objects are kept in a circular list */ struct nameserver *next, *prev; struct event timeout_event; /* used to keep the timeout for */ /* when we next probe this server. */ /* Valid if state == 0 */ /* Outstanding probe request for this nameserver, if any */ struct evdns_request *probe_request; char state; /* zero if we think that this server is down */ char choked; /* true if we have an EAGAIN from this server's socket */ char write_waiting; /* true if we are waiting for EV_WRITE events */ struct evdns_base *base; /* Number of currently inflight requests: used * to track when we should add/del the event. */ int requests_inflight; }; /* Represents a local port where we're listening for DNS requests. Right now, */ /* only UDP is supported. */ struct evdns_server_port { evutil_socket_t socket; /* socket we use to read queries and write replies. */ int refcnt; /* reference count. */ char choked; /* Are we currently blocked from writing? */ char closing; /* Are we trying to close this port, pending writes? */ evdns_request_callback_fn_type user_callback; /* Fn to handle requests */ void *user_data; /* Opaque pointer passed to user_callback */ struct event event; /* Read/write event */ /* circular list of replies that we want to write. */ struct server_request *pending_replies; struct event_base *event_base; #ifndef EVENT__DISABLE_THREAD_SUPPORT void *lock; #endif }; /* Represents part of a reply being built. (That is, a single RR.) */ struct server_reply_item { struct server_reply_item *next; /* next item in sequence. */ char *name; /* name part of the RR */ u16 type; /* The RR type */ u16 class; /* The RR class (usually CLASS_INET) */ u32 ttl; /* The RR TTL */ char is_name; /* True iff data is a label */ u16 datalen; /* Length of data; -1 if data is a label */ void *data; /* The contents of the RR */ }; /* Represents a request that we've received as a DNS server, and holds */ /* the components of the reply as we're constructing it. */ struct server_request { /* Pointers to the next and previous entries on the list of replies */ /* that we're waiting to write. Only set if we have tried to respond */ /* and gotten EAGAIN. */ struct server_request *next_pending; struct server_request *prev_pending; u16 trans_id; /* Transaction id. */ struct evdns_server_port *port; /* Which port received this request on? */ struct sockaddr_storage addr; /* Where to send the response */ ev_socklen_t addrlen; /* length of addr */ int n_answer; /* how many answer RRs have been set? */ int n_authority; /* how many authority RRs have been set? */ int n_additional; /* how many additional RRs have been set? */ struct server_reply_item *answer; /* linked list of answer RRs */ struct server_reply_item *authority; /* linked list of authority RRs */ struct server_reply_item *additional; /* linked list of additional RRs */ /* Constructed response. Only set once we're ready to send a reply. */ /* Once this is set, the RR fields are cleared, and no more should be set. */ char *response; size_t response_len; /* Caller-visible fields: flags, questions. */ struct evdns_server_request base; }; struct evdns_base { /* An array of n_req_heads circular lists for inflight requests. * Each inflight request req is in req_heads[req->trans_id % n_req_heads]. */ struct request **req_heads; /* A circular list of requests that we're waiting to send, but haven't * sent yet because there are too many requests inflight */ struct request *req_waiting_head; /* A circular list of nameservers. */ struct nameserver *server_head; int n_req_heads; struct event_base *event_base; /* The number of good nameservers that we have */ int global_good_nameservers; /* inflight requests are contained in the req_head list */ /* and are actually going out across the network */ int global_requests_inflight; /* requests which aren't inflight are in the waiting list */ /* and are counted here */ int global_requests_waiting; int global_max_requests_inflight; struct timeval global_timeout; /* 5 seconds by default */ int global_max_reissues; /* a reissue occurs when we get some errors from the server */ int global_max_retransmits; /* number of times we'll retransmit a request which timed out */ /* number of timeouts in a row before we consider this server to be down */ int global_max_nameserver_timeout; /* true iff we will use the 0x20 hack to prevent poisoning attacks. */ int global_randomize_case; /* The first time that a nameserver fails, how long do we wait before * probing to see if it has returned? */ struct timeval global_nameserver_probe_initial_timeout; /** Port to bind to for outgoing DNS packets. */ struct sockaddr_storage global_outgoing_address; /** ev_socklen_t for global_outgoing_address. 0 if it isn't set. */ ev_socklen_t global_outgoing_addrlen; struct timeval global_getaddrinfo_allow_skew; int so_rcvbuf; int so_sndbuf; int getaddrinfo_ipv4_timeouts; int getaddrinfo_ipv6_timeouts; int getaddrinfo_ipv4_answered; int getaddrinfo_ipv6_answered; struct search_state *global_search_state; TAILQ_HEAD(hosts_list, hosts_entry) hostsdb; #ifndef EVENT__DISABLE_THREAD_SUPPORT void *lock; #endif int disable_when_inactive; }; struct hosts_entry { TAILQ_ENTRY(hosts_entry) next; union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; } addr; int addrlen; char hostname[1]; }; static struct evdns_base *current_base = NULL; struct evdns_base * evdns_get_global_base(void) { return current_base; } /* Given a pointer to an evdns_server_request, get the corresponding */ /* server_request. */ #define TO_SERVER_REQUEST(base_ptr) \ ((struct server_request*) \ (((char*)(base_ptr) - evutil_offsetof(struct server_request, base)))) #define REQ_HEAD(base, id) ((base)->req_heads[id % (base)->n_req_heads]) static struct nameserver *nameserver_pick(struct evdns_base *base); static void evdns_request_insert(struct request *req, struct request **head); static void evdns_request_remove(struct request *req, struct request **head); static void nameserver_ready_callback(evutil_socket_t fd, short events, void *arg); static int evdns_transmit(struct evdns_base *base); static int evdns_request_transmit(struct request *req); static void nameserver_send_probe(struct nameserver *const ns); static void search_request_finished(struct evdns_request *const); static int search_try_next(struct evdns_request *const req); static struct request *search_request_new(struct evdns_base *base, struct evdns_request *handle, int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg); static void evdns_requests_pump_waiting_queue(struct evdns_base *base); static u16 transaction_id_pick(struct evdns_base *base); static struct request *request_new(struct evdns_base *base, struct evdns_request *handle, int type, const char *name, int flags, evdns_callback_type callback, void *ptr); static void request_submit(struct request *const req); static int server_request_free(struct server_request *req); static void server_request_free_answers(struct server_request *req); static void server_port_free(struct evdns_server_port *port); static void server_port_ready_callback(evutil_socket_t fd, short events, void *arg); static int evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags, const char *const filename); static int evdns_base_set_option_impl(struct evdns_base *base, const char *option, const char *val, int flags); static void evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests); static void evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg); static int strtoint(const char *const str); #ifdef EVENT__DISABLE_THREAD_SUPPORT #define EVDNS_LOCK(base) EVUTIL_NIL_STMT_ #define EVDNS_UNLOCK(base) EVUTIL_NIL_STMT_ #define ASSERT_LOCKED(base) EVUTIL_NIL_STMT_ #else #define EVDNS_LOCK(base) \ EVLOCK_LOCK((base)->lock, 0) #define EVDNS_UNLOCK(base) \ EVLOCK_UNLOCK((base)->lock, 0) #define ASSERT_LOCKED(base) \ EVLOCK_ASSERT_LOCKED((base)->lock) #endif static evdns_debug_log_fn_type evdns_log_fn = NULL; void evdns_set_log_fn(evdns_debug_log_fn_type fn) { evdns_log_fn = fn; } #ifdef __GNUC__ #define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3))) #else #define EVDNS_LOG_CHECK #endif static void evdns_log_(int severity, const char *fmt, ...) EVDNS_LOG_CHECK; static void evdns_log_(int severity, const char *fmt, ...) { va_list args; va_start(args,fmt); if (evdns_log_fn) { char buf[512]; int is_warn = (severity == EVDNS_LOG_WARN); evutil_vsnprintf(buf, sizeof(buf), fmt, args); evdns_log_fn(is_warn, buf); } else { event_logv_(severity, NULL, fmt, args); } va_end(args); } #define log evdns_log_ /* This walks the list of inflight requests to find the */ /* one with a matching transaction id. Returns NULL on */ /* failure */ static struct request * request_find_from_trans_id(struct evdns_base *base, u16 trans_id) { struct request *req = REQ_HEAD(base, trans_id); struct request *const started_at = req; ASSERT_LOCKED(base); if (req) { do { if (req->trans_id == trans_id) return req; req = req->next; } while (req != started_at); } return NULL; } /* a libevent callback function which is called when a nameserver */ /* has gone down and we want to test if it has came back to life yet */ static void nameserver_prod_callback(evutil_socket_t fd, short events, void *arg) { struct nameserver *const ns = (struct nameserver *) arg; (void)fd; (void)events; EVDNS_LOCK(ns->base); nameserver_send_probe(ns); EVDNS_UNLOCK(ns->base); } /* a libevent callback which is called when a nameserver probe (to see if */ /* it has come back to life) times out. We increment the count of failed_times */ /* and wait longer to send the next probe packet. */ static void nameserver_probe_failed(struct nameserver *const ns) { struct timeval timeout; int i; ASSERT_LOCKED(ns->base); (void) evtimer_del(&ns->timeout_event); if (ns->state == 1) { /* This can happen if the nameserver acts in a way which makes us mark */ /* it as bad and then starts sending good replies. */ return; } #define MAX_PROBE_TIMEOUT 3600 #define TIMEOUT_BACKOFF_FACTOR 3 memcpy(&timeout, &ns->base->global_nameserver_probe_initial_timeout, sizeof(struct timeval)); for (i=ns->failed_times; i > 0 && timeout.tv_sec < MAX_PROBE_TIMEOUT; --i) { timeout.tv_sec *= TIMEOUT_BACKOFF_FACTOR; timeout.tv_usec *= TIMEOUT_BACKOFF_FACTOR; if (timeout.tv_usec > 1000000) { timeout.tv_sec += timeout.tv_usec / 1000000; timeout.tv_usec %= 1000000; } } if (timeout.tv_sec > MAX_PROBE_TIMEOUT) { timeout.tv_sec = MAX_PROBE_TIMEOUT; timeout.tv_usec = 0; } ns->failed_times++; if (evtimer_add(&ns->timeout_event, &timeout) < 0) { char addrbuf[128]; log(EVDNS_LOG_WARN, "Error from libevent when adding timer event for %s", evutil_format_sockaddr_port_( (struct sockaddr *)&ns->address, addrbuf, sizeof(addrbuf))); } } static void request_swap_ns(struct request *req, struct nameserver *ns) { if (ns && req->ns != ns) { EVUTIL_ASSERT(req->ns->requests_inflight > 0); req->ns->requests_inflight--; ns->requests_inflight++; req->ns = ns; } } /* called when a nameserver has been deemed to have failed. For example, too */ /* many packets have timed out etc */ static void nameserver_failed(struct nameserver *const ns, const char *msg) { struct request *req, *started_at; struct evdns_base *base = ns->base; int i; char addrbuf[128]; ASSERT_LOCKED(base); /* if this nameserver has already been marked as failed */ /* then don't do anything */ if (!ns->state) return; log(EVDNS_LOG_MSG, "Nameserver %s has failed: %s", evutil_format_sockaddr_port_( (struct sockaddr *)&ns->address, addrbuf, sizeof(addrbuf)), msg); base->global_good_nameservers--; EVUTIL_ASSERT(base->global_good_nameservers >= 0); if (base->global_good_nameservers == 0) { log(EVDNS_LOG_MSG, "All nameservers have failed"); } ns->state = 0; ns->failed_times = 1; if (evtimer_add(&ns->timeout_event, &base->global_nameserver_probe_initial_timeout) < 0) { log(EVDNS_LOG_WARN, "Error from libevent when adding timer event for %s", evutil_format_sockaddr_port_( (struct sockaddr *)&ns->address, addrbuf, sizeof(addrbuf))); /* ???? Do more? */ } /* walk the list of inflight requests to see if any can be reassigned to */ /* a different server. Requests in the waiting queue don't have a */ /* nameserver assigned yet */ /* if we don't have *any* good nameservers then there's no point */ /* trying to reassign requests to one */ if (!base->global_good_nameservers) return; for (i = 0; i < base->n_req_heads; ++i) { req = started_at = base->req_heads[i]; if (req) { do { if (req->tx_count == 0 && req->ns == ns) { /* still waiting to go out, can be moved */ /* to another server */ request_swap_ns(req, nameserver_pick(base)); } req = req->next; } while (req != started_at); } } } static void nameserver_up(struct nameserver *const ns) { char addrbuf[128]; ASSERT_LOCKED(ns->base); if (ns->state) return; log(EVDNS_LOG_MSG, "Nameserver %s is back up", evutil_format_sockaddr_port_( (struct sockaddr *)&ns->address, addrbuf, sizeof(addrbuf))); evtimer_del(&ns->timeout_event); if (ns->probe_request) { evdns_cancel_request(ns->base, ns->probe_request); ns->probe_request = NULL; } ns->state = 1; ns->failed_times = 0; ns->timedout = 0; ns->base->global_good_nameservers++; } static void request_trans_id_set(struct request *const req, const u16 trans_id) { req->trans_id = trans_id; *((u16 *) req->request) = htons(trans_id); } /* Called to remove a request from a list and dealloc it. */ /* head is a pointer to the head of the list it should be */ /* removed from or NULL if the request isn't in a list. */ /* when free_handle is one, free the handle as well. */ static void request_finished(struct request *const req, struct request **head, int free_handle) { struct evdns_base *base = req->base; int was_inflight = (head != &base->req_waiting_head); EVDNS_LOCK(base); ASSERT_VALID_REQUEST(req); if (head) evdns_request_remove(req, head); log(EVDNS_LOG_DEBUG, "Removing timeout for request %p", req); if (was_inflight) { evtimer_del(&req->timeout_event); base->global_requests_inflight--; req->ns->requests_inflight--; } else { base->global_requests_waiting--; } /* it was initialized during request_new / evtimer_assign */ event_debug_unassign(&req->timeout_event); if (req->ns && req->ns->requests_inflight == 0 && req->base->disable_when_inactive) { event_del(&req->ns->event); evtimer_del(&req->ns->timeout_event); } if (!req->request_appended) { /* need to free the request data on it's own */ mm_free(req->request); } else { /* the request data is appended onto the header */ /* so everything gets free()ed when we: */ } if (req->handle) { EVUTIL_ASSERT(req->handle->current_req == req); if (free_handle) { search_request_finished(req->handle); req->handle->current_req = NULL; if (! req->handle->pending_cb) { /* If we're planning to run the callback, * don't free the handle until later. */ mm_free(req->handle); } req->handle = NULL; /* If we have a bug, let's crash * early */ } else { req->handle->current_req = NULL; } } mm_free(req); evdns_requests_pump_waiting_queue(base); EVDNS_UNLOCK(base); } /* This is called when a server returns a funny error code. */ /* We try the request again with another server. */ /* */ /* return: */ /* 0 ok */ /* 1 failed/reissue is pointless */ static int request_reissue(struct request *req) { const struct nameserver *const last_ns = req->ns; ASSERT_LOCKED(req->base); ASSERT_VALID_REQUEST(req); /* the last nameserver should have been marked as failing */ /* by the caller of this function, therefore pick will try */ /* not to return it */ request_swap_ns(req, nameserver_pick(req->base)); if (req->ns == last_ns) { /* ... but pick did return it */ /* not a lot of point in trying again with the */ /* same server */ return 1; } req->reissue_count++; req->tx_count = 0; req->transmit_me = 1; return 0; } /* this function looks for space on the inflight queue and promotes */ /* requests from the waiting queue if it can. */ /* */ /* TODO: */ /* add return code, see at nameserver_pick() and other functions. */ static void evdns_requests_pump_waiting_queue(struct evdns_base *base) { ASSERT_LOCKED(base); while (base->global_requests_inflight < base->global_max_requests_inflight && base->global_requests_waiting) { struct request *req; EVUTIL_ASSERT(base->req_waiting_head); req = base->req_waiting_head; req->ns = nameserver_pick(base); if (!req->ns) return; /* move a request from the waiting queue to the inflight queue */ req->ns->requests_inflight++; evdns_request_remove(req, &base->req_waiting_head); base->global_requests_waiting--; base->global_requests_inflight++; request_trans_id_set(req, transaction_id_pick(base)); evdns_request_insert(req, &REQ_HEAD(base, req->trans_id)); evdns_request_transmit(req); evdns_transmit(base); } } /* TODO(nickm) document */ struct deferred_reply_callback { struct event_callback deferred; struct evdns_request *handle; u8 request_type; u8 have_reply; u32 ttl; u32 err; evdns_callback_type user_callback; struct reply reply; }; static void reply_run_callback(struct event_callback *d, void *user_pointer) { struct deferred_reply_callback *cb = EVUTIL_UPCAST(d, struct deferred_reply_callback, deferred); switch (cb->request_type) { case TYPE_A: if (cb->have_reply) cb->user_callback(DNS_ERR_NONE, DNS_IPv4_A, cb->reply.data.a.addrcount, cb->ttl, cb->reply.data.a.addresses, user_pointer); else cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer); break; case TYPE_PTR: if (cb->have_reply) { char *name = cb->reply.data.ptr.name; cb->user_callback(DNS_ERR_NONE, DNS_PTR, 1, cb->ttl, &name, user_pointer); } else { cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer); } break; case TYPE_AAAA: if (cb->have_reply) cb->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA, cb->reply.data.aaaa.addrcount, cb->ttl, cb->reply.data.aaaa.addresses, user_pointer); else cb->user_callback(cb->err, 0, 0, cb->ttl, NULL, user_pointer); break; default: EVUTIL_ASSERT(0); } if (cb->handle && cb->handle->pending_cb) { mm_free(cb->handle); } mm_free(cb); } static void reply_schedule_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) { struct deferred_reply_callback *d = mm_calloc(1, sizeof(*d)); if (!d) { event_warn("%s: Couldn't allocate space for deferred callback.", __func__); return; } ASSERT_LOCKED(req->base); d->request_type = req->request_type; d->user_callback = req->user_callback; d->ttl = ttl; d->err = err; if (reply) { d->have_reply = 1; memcpy(&d->reply, reply, sizeof(struct reply)); } if (req->handle) { req->handle->pending_cb = 1; d->handle = req->handle; } event_deferred_cb_init_( &d->deferred, event_get_priority(&req->timeout_event), reply_run_callback, req->user_pointer); event_deferred_cb_schedule_( req->base->event_base, &d->deferred); } #define _QR_MASK 0x8000U #define _OP_MASK 0x7800U #define _AA_MASK 0x0400U #define _TC_MASK 0x0200U #define _RD_MASK 0x0100U #define _RA_MASK 0x0080U #define _Z_MASK 0x0040U #define _AD_MASK 0x0020U #define _CD_MASK 0x0010U #define _RCODE_MASK 0x000fU #define _Z_MASK_DEPRECATED 0x0070U /* this processes a parsed reply packet */ static void reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) { int error; char addrbuf[128]; static const int error_codes[] = { DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED }; ASSERT_LOCKED(req->base); ASSERT_VALID_REQUEST(req); if (flags & (_RCODE_MASK | _TC_MASK) || !reply || !reply->have_answer) { /* there was an error */ if (flags & _TC_MASK) { error = DNS_ERR_TRUNCATED; } else if (flags & _RCODE_MASK) { u16 error_code = (flags & _RCODE_MASK) - 1; if (error_code > 4) { error = DNS_ERR_UNKNOWN; } else { error = error_codes[error_code]; } } else if (reply && !reply->have_answer) { error = DNS_ERR_NODATA; } else { error = DNS_ERR_UNKNOWN; } switch (error) { case DNS_ERR_NOTIMPL: case DNS_ERR_REFUSED: /* we regard these errors as marking a bad nameserver */ if (req->reissue_count < req->base->global_max_reissues) { char msg[64]; evutil_snprintf(msg, sizeof(msg), "Bad response %d (%s)", error, evdns_err_to_string(error)); nameserver_failed(req->ns, msg); if (!request_reissue(req)) return; } break; case DNS_ERR_SERVERFAILED: /* rcode 2 (servfailed) sometimes means "we * are broken" and sometimes (with some binds) * means "that request was very confusing." * Treat this as a timeout, not a failure. */ log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver" "at %s; will allow the request to time out.", evutil_format_sockaddr_port_( (struct sockaddr *)&req->ns->address, addrbuf, sizeof(addrbuf))); /* Call the timeout function */ evdns_request_timeout_callback(0, 0, req); return; default: /* we got a good reply from the nameserver: it is up. */ if (req->handle == req->ns->probe_request) { /* Avoid double-free */ req->ns->probe_request = NULL; } nameserver_up(req->ns); } if (req->handle->search_state && req->request_type != TYPE_PTR) { /* if we have a list of domains to search in, * try the next one */ if (!search_try_next(req->handle)) { /* a new request was issued so this * request is finished and */ /* the user callback will be made when * that request (or a */ /* child of it) finishes. */ return; } } /* all else failed. Pass the failure up */ reply_schedule_callback(req, ttl, error, NULL); request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1); } else { /* all ok, tell the user */ reply_schedule_callback(req, ttl, 0, reply); if (req->handle == req->ns->probe_request) req->ns->probe_request = NULL; /* Avoid double-free */ nameserver_up(req->ns); request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1); } } static int name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) { int name_end = -1; int j = *idx; int ptr_count = 0; #define GET32(x) do { if (j + 4 > length) goto err; memcpy(&t32_, packet + j, 4); j += 4; x = ntohl(t32_); } while (0) #define GET16(x) do { if (j + 2 > length) goto err; memcpy(&t_, packet + j, 2); j += 2; x = ntohs(t_); } while (0) #define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while (0) char *cp = name_out; const char *const end = name_out + name_out_len; /* Normally, names are a series of length prefixed strings terminated */ /* with a length of 0 (the lengths are u8's < 63). */ /* However, the length can start with a pair of 1 bits and that */ /* means that the next 14 bits are a pointer within the current */ /* packet. */ for (;;) { u8 label_len; GET8(label_len); if (!label_len) break; if (label_len & 0xc0) { u8 ptr_low; GET8(ptr_low); if (name_end < 0) name_end = j; j = (((int)label_len & 0x3f) << 8) + ptr_low; /* Make sure that the target offset is in-bounds. */ if (j < 0 || j >= length) return -1; /* If we've jumped more times than there are characters in the * message, we must have a loop. */ if (++ptr_count > length) return -1; continue; } if (label_len > 63) return -1; if (cp != name_out) { if (cp + 1 >= end) return -1; *cp++ = '.'; } if (cp + label_len >= end) return -1; if (j + label_len > length) return -1; memcpy(cp, packet + j, label_len); cp += label_len; j += label_len; } if (cp >= end) return -1; *cp = '\0'; if (name_end < 0) *idx = j; else *idx = name_end; return 0; err: return -1; } /* parses a raw request from a nameserver */ static int reply_parse(struct evdns_base *base, u8 *packet, int length) { int j = 0, k = 0; /* index into packet */ u16 t_; /* used by the macros */ u32 t32_; /* used by the macros */ char tmp_name[256], cmp_name[256]; /* used by the macros */ int name_matches = 0; u16 trans_id, questions, answers, authority, additional, datalength; u16 flags = 0; u32 ttl, ttl_r = 0xffffffff; struct reply reply; struct request *req = NULL; unsigned int i; ASSERT_LOCKED(base); GET16(trans_id); GET16(flags); GET16(questions); GET16(answers); GET16(authority); GET16(additional); (void) authority; /* suppress "unused variable" warnings. */ (void) additional; /* suppress "unused variable" warnings. */ req = request_find_from_trans_id(base, trans_id); if (!req) return -1; EVUTIL_ASSERT(req->base == base); memset(&reply, 0, sizeof(reply)); /* If it's not an answer, it doesn't correspond to any request. */ if (!(flags & _QR_MASK)) return -1; /* must be an answer */ if ((flags & (_RCODE_MASK|_TC_MASK)) && (flags & (_RCODE_MASK|_TC_MASK)) != DNS_ERR_NOTEXIST) { /* there was an error and it's not NXDOMAIN */ goto err; } /* if (!answers) return; */ /* must have an answer of some form */ /* This macro skips a name in the DNS reply. */ #define SKIP_NAME \ do { tmp_name[0] = '\0'; \ if (name_parse(packet, length, &j, tmp_name, \ sizeof(tmp_name))<0) \ goto err; \ } while (0) reply.type = req->request_type; /* skip over each question in the reply */ for (i = 0; i < questions; ++i) { /* the question looks like * */ tmp_name[0] = '\0'; cmp_name[0] = '\0'; k = j; if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name)) < 0) goto err; if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) goto err; if (!base->global_randomize_case) { if (strcmp(tmp_name, cmp_name) == 0) name_matches = 1; } else { if (evutil_ascii_strcasecmp(tmp_name, cmp_name) == 0) name_matches = 1; } j += 4; if (j > length) goto err; } if (!name_matches) goto err; /* now we have the answer section which looks like * */ for (i = 0; i < answers; ++i) { u16 type, class; SKIP_NAME; GET16(type); GET16(class); GET32(ttl); GET16(datalength); if (type == TYPE_A && class == CLASS_INET) { int addrcount, addrtocopy; if (req->request_type != TYPE_A) { j += datalength; continue; } if ((datalength & 3) != 0) /* not an even number of As. */ goto err; addrcount = datalength >> 2; addrtocopy = MIN(MAX_V4_ADDRS - reply.data.a.addrcount, (unsigned)addrcount); ttl_r = MIN(ttl_r, ttl); /* we only bother with the first four addresses. */ if (j + 4*addrtocopy > length) goto err; memcpy(&reply.data.a.addresses[reply.data.a.addrcount], packet + j, 4*addrtocopy); j += 4*addrtocopy; reply.data.a.addrcount += addrtocopy; reply.have_answer = 1; if (reply.data.a.addrcount == MAX_V4_ADDRS) break; } else if (type == TYPE_PTR && class == CLASS_INET) { if (req->request_type != TYPE_PTR) { j += datalength; continue; } if (name_parse(packet, length, &j, reply.data.ptr.name, sizeof(reply.data.ptr.name))<0) goto err; ttl_r = MIN(ttl_r, ttl); reply.have_answer = 1; break; } else if (type == TYPE_CNAME) { char cname[HOST_NAME_MAX]; if (!req->put_cname_in_ptr || *req->put_cname_in_ptr) { j += datalength; continue; } if (name_parse(packet, length, &j, cname, sizeof(cname))<0) goto err; *req->put_cname_in_ptr = mm_strdup(cname); } else if (type == TYPE_AAAA && class == CLASS_INET) { int addrcount, addrtocopy; if (req->request_type != TYPE_AAAA) { j += datalength; continue; } if ((datalength & 15) != 0) /* not an even number of AAAAs. */ goto err; addrcount = datalength >> 4; /* each address is 16 bytes long */ addrtocopy = MIN(MAX_V6_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount); ttl_r = MIN(ttl_r, ttl); /* we only bother with the first four addresses. */ if (j + 16*addrtocopy > length) goto err; memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount], packet + j, 16*addrtocopy); reply.data.aaaa.addrcount += addrtocopy; j += 16*addrtocopy; reply.have_answer = 1; if (reply.data.aaaa.addrcount == MAX_V6_ADDRS) break; } else { /* skip over any other type of resource */ j += datalength; } } if (!reply.have_answer) { for (i = 0; i < authority; ++i) { u16 type, class; SKIP_NAME; GET16(type); GET16(class); GET32(ttl); GET16(datalength); if (type == TYPE_SOA && class == CLASS_INET) { u32 serial, refresh, retry, expire, minimum; SKIP_NAME; SKIP_NAME; GET32(serial); GET32(refresh); GET32(retry); GET32(expire); GET32(minimum); (void)expire; (void)retry; (void)refresh; (void)serial; ttl_r = MIN(ttl_r, ttl); ttl_r = MIN(ttl_r, minimum); } else { /* skip over any other type of resource */ j += datalength; } } } if (ttl_r == 0xffffffff) ttl_r = 0; reply_handle(req, flags, ttl_r, &reply); return 0; err: if (req) reply_handle(req, flags, 0, NULL); return -1; } /* Parse a raw request (packet,length) sent to a nameserver port (port) from */ /* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */ /* callback. */ static int request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, ev_socklen_t addrlen) { int j = 0; /* index into packet */ u16 t_; /* used by the macros */ char tmp_name[256]; /* used by the macros */ int i; u16 trans_id, flags, questions, answers, authority, additional; struct server_request *server_req = NULL; ASSERT_LOCKED(port); /* Get the header fields */ GET16(trans_id); GET16(flags); GET16(questions); GET16(answers); GET16(authority); GET16(additional); (void)answers; (void)additional; (void)authority; if (flags & _QR_MASK) return -1; /* Must not be an answer. */ flags &= (_RD_MASK|_CD_MASK); /* Only RD and CD get preserved. */ server_req = mm_malloc(sizeof(struct server_request)); if (server_req == NULL) return -1; memset(server_req, 0, sizeof(struct server_request)); server_req->trans_id = trans_id; memcpy(&server_req->addr, addr, addrlen); server_req->addrlen = addrlen; server_req->base.flags = flags; server_req->base.nquestions = 0; server_req->base.questions = mm_calloc(sizeof(struct evdns_server_question *), questions); if (server_req->base.questions == NULL) goto err; for (i = 0; i < questions; ++i) { u16 type, class; struct evdns_server_question *q; int namelen; if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) goto err; GET16(type); GET16(class); namelen = (int)strlen(tmp_name); q = mm_malloc(sizeof(struct evdns_server_question) + namelen); if (!q) goto err; q->type = type; q->dns_question_class = class; memcpy(q->name, tmp_name, namelen+1); server_req->base.questions[server_req->base.nquestions++] = q; } /* Ignore answers, authority, and additional. */ server_req->port = port; port->refcnt++; /* Only standard queries are supported. */ if (flags & _OP_MASK) { evdns_server_request_respond(&(server_req->base), DNS_ERR_NOTIMPL); return -1; } port->user_callback(&(server_req->base), port->user_data); return 0; err: if (server_req->base.questions) { for (i = 0; i < server_req->base.nquestions; ++i) mm_free(server_req->base.questions[i]); mm_free(server_req->base.questions); } mm_free(server_req); return -1; #undef SKIP_NAME #undef GET32 #undef GET16 #undef GET8 } void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)) { } void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)) { } /* Try to choose a strong transaction id which isn't already in flight */ static u16 transaction_id_pick(struct evdns_base *base) { ASSERT_LOCKED(base); for (;;) { u16 trans_id; evutil_secure_rng_get_bytes(&trans_id, sizeof(trans_id)); if (trans_id == 0xffff) continue; /* now check to see if that id is already inflight */ if (request_find_from_trans_id(base, trans_id) == NULL) return trans_id; } } /* choose a namesever to use. This function will try to ignore */ /* nameservers which we think are down and load balance across the rest */ /* by updating the server_head global each time. */ static struct nameserver * nameserver_pick(struct evdns_base *base) { struct nameserver *started_at = base->server_head, *picked; ASSERT_LOCKED(base); if (!base->server_head) return NULL; /* if we don't have any good nameservers then there's no */ /* point in trying to find one. */ if (!base->global_good_nameservers) { base->server_head = base->server_head->next; return base->server_head; } /* remember that nameservers are in a circular list */ for (;;) { if (base->server_head->state) { /* we think this server is currently good */ picked = base->server_head; base->server_head = base->server_head->next; return picked; } base->server_head = base->server_head->next; if (base->server_head == started_at) { /* all the nameservers seem to be down */ /* so we just return this one and hope for the */ /* best */ EVUTIL_ASSERT(base->global_good_nameservers == 0); picked = base->server_head; base->server_head = base->server_head->next; return picked; } } } /* this is called when a namesever socket is ready for reading */ static void nameserver_read(struct nameserver *ns) { struct sockaddr_storage ss; ev_socklen_t addrlen = sizeof(ss); u8 packet[1500]; char addrbuf[128]; ASSERT_LOCKED(ns->base); for (;;) { const int r = recvfrom(ns->socket, (void*)packet, sizeof(packet), 0, (struct sockaddr*)&ss, &addrlen); if (r < 0) { int err = evutil_socket_geterror(ns->socket); if (EVUTIL_ERR_RW_RETRIABLE(err)) return; nameserver_failed(ns, evutil_socket_error_to_string(err)); return; } if (evutil_sockaddr_cmp((struct sockaddr*)&ss, (struct sockaddr*)&ns->address, 0)) { log(EVDNS_LOG_WARN, "Address mismatch on received " "DNS packet. Apparent source was %s", evutil_format_sockaddr_port_( (struct sockaddr *)&ss, addrbuf, sizeof(addrbuf))); return; } ns->timedout = 0; reply_parse(ns->base, packet, r); } } /* Read a packet from a DNS client on a server port s, parse it, and */ /* act accordingly. */ static void server_port_read(struct evdns_server_port *s) { u8 packet[1500]; struct sockaddr_storage addr; ev_socklen_t addrlen; int r; ASSERT_LOCKED(s); for (;;) { addrlen = sizeof(struct sockaddr_storage); r = recvfrom(s->socket, (void*)packet, sizeof(packet), 0, (struct sockaddr*) &addr, &addrlen); if (r < 0) { int err = evutil_socket_geterror(s->socket); if (EVUTIL_ERR_RW_RETRIABLE(err)) return; log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.", evutil_socket_error_to_string(err), err); return; } request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen); } } /* Try to write all pending replies on a given DNS server port. */ static void server_port_flush(struct evdns_server_port *port) { struct server_request *req = port->pending_replies; ASSERT_LOCKED(port); while (req) { int r = sendto(port->socket, req->response, (int)req->response_len, 0, (struct sockaddr*) &req->addr, (ev_socklen_t)req->addrlen); if (r < 0) { int err = evutil_socket_geterror(port->socket); if (EVUTIL_ERR_RW_RETRIABLE(err)) return; log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", evutil_socket_error_to_string(err), err); } if (server_request_free(req)) { /* we released the last reference to req->port. */ return; } else { EVUTIL_ASSERT(req != port->pending_replies); req = port->pending_replies; } } /* We have no more pending requests; stop listening for 'writeable' events. */ (void) event_del(&port->event); event_assign(&port->event, port->event_base, port->socket, EV_READ | EV_PERSIST, server_port_ready_callback, port); if (event_add(&port->event, NULL) < 0) { log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server."); /* ???? Do more? */ } } /* set if we are waiting for the ability to write to this server. */ /* if waiting is true then we ask libevent for EV_WRITE events, otherwise */ /* we stop these events. */ static void nameserver_write_waiting(struct nameserver *ns, char waiting) { ASSERT_LOCKED(ns->base); if (ns->write_waiting == waiting) return; ns->write_waiting = waiting; (void) event_del(&ns->event); event_assign(&ns->event, ns->base->event_base, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST, nameserver_ready_callback, ns); if (event_add(&ns->event, NULL) < 0) { char addrbuf[128]; log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s", evutil_format_sockaddr_port_( (struct sockaddr *)&ns->address, addrbuf, sizeof(addrbuf))); /* ???? Do more? */ } } /* a callback function. Called by libevent when the kernel says that */ /* a nameserver socket is ready for writing or reading */ static void nameserver_ready_callback(evutil_socket_t fd, short events, void *arg) { struct nameserver *ns = (struct nameserver *) arg; (void)fd; EVDNS_LOCK(ns->base); if (events & EV_WRITE) { ns->choked = 0; if (!evdns_transmit(ns->base)) { nameserver_write_waiting(ns, 0); } } if (events & EV_READ) { nameserver_read(ns); } EVDNS_UNLOCK(ns->base); } /* a callback function. Called by libevent when the kernel says that */ /* a server socket is ready for writing or reading. */ static void server_port_ready_callback(evutil_socket_t fd, short events, void *arg) { struct evdns_server_port *port = (struct evdns_server_port *) arg; (void) fd; EVDNS_LOCK(port); if (events & EV_WRITE) { port->choked = 0; server_port_flush(port); } if (events & EV_READ) { server_port_read(port); } EVDNS_UNLOCK(port); } /* This is an inefficient representation; only use it via the dnslabel_table_* * functions, so that is can be safely replaced with something smarter later. */ #define MAX_LABELS 128 /* Structures used to implement name compression */ struct dnslabel_entry { char *v; off_t pos; }; struct dnslabel_table { int n_labels; /* number of current entries */ /* map from name to position in message */ struct dnslabel_entry labels[MAX_LABELS]; }; /* Initialize dnslabel_table. */ static void dnslabel_table_init(struct dnslabel_table *table) { table->n_labels = 0; } /* Free all storage held by table, but not the table itself. */ static void dnslabel_clear(struct dnslabel_table *table) { int i; for (i = 0; i < table->n_labels; ++i) mm_free(table->labels[i].v); table->n_labels = 0; } /* return the position of the label in the current message, or -1 if the label */ /* hasn't been used yet. */ static int dnslabel_table_get_pos(const struct dnslabel_table *table, const char *label) { int i; for (i = 0; i < table->n_labels; ++i) { if (!strcmp(label, table->labels[i].v)) return table->labels[i].pos; } return -1; } /* remember that we've used the label at position pos */ static int dnslabel_table_add(struct dnslabel_table *table, const char *label, off_t pos) { char *v; int p; if (table->n_labels == MAX_LABELS) return (-1); v = mm_strdup(label); if (v == NULL) return (-1); p = table->n_labels++; table->labels[p].v = v; table->labels[p].pos = pos; return (0); } /* Converts a string to a length-prefixed set of DNS labels, starting */ /* at buf[j]. name and buf must not overlap. name_len should be the length */ /* of name. table is optional, and is used for compression. */ /* */ /* Input: abc.def */ /* Output: <3>abc<3>def<0> */ /* */ /* Returns the first index after the encoded name, or negative on error. */ /* -1 label was > 63 bytes */ /* -2 name too long to fit in buffer. */ /* */ static off_t dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j, const char *name, const size_t name_len, struct dnslabel_table *table) { const char *end = name + name_len; int ref = 0; u16 t_; #define APPEND16(x) do { \ if (j + 2 > (off_t)buf_len) \ goto overflow; \ t_ = htons(x); \ memcpy(buf + j, &t_, 2); \ j += 2; \ } while (0) #define APPEND32(x) do { \ if (j + 4 > (off_t)buf_len) \ goto overflow; \ t32_ = htonl(x); \ memcpy(buf + j, &t32_, 4); \ j += 4; \ } while (0) if (name_len > 255) return -2; for (;;) { const char *const start = name; if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) { APPEND16(ref | 0xc000); return j; } name = strchr(name, '.'); if (!name) { const size_t label_len = end - start; if (label_len > 63) return -1; if ((size_t)(j+label_len+1) > buf_len) return -2; if (table) dnslabel_table_add(table, start, j); buf[j++] = (ev_uint8_t)label_len; memcpy(buf + j, start, label_len); j += (int) label_len; break; } else { /* append length of the label. */ const size_t label_len = name - start; if (label_len > 63) return -1; if ((size_t)(j+label_len+1) > buf_len) return -2; if (table) dnslabel_table_add(table, start, j); buf[j++] = (ev_uint8_t)label_len; memcpy(buf + j, start, label_len); j += (int) label_len; /* hop over the '.' */ name++; } } /* the labels must be terminated by a 0. */ /* It's possible that the name ended in a . */ /* in which case the zero is already there */ if ((size_t)j >= buf_len) return -2; if (!j || buf[j-1]) buf[j++] = 0; return j; overflow: return (-2); } /* Finds the length of a dns request for a DNS name of the given */ /* length. The actual request may be smaller than the value returned */ /* here */ static size_t evdns_request_len(const size_t name_len) { return 96 + /* length of the DNS standard header */ name_len + 2 + 4; /* space for the resource type */ } /* build a dns request packet into buf. buf should be at least as long */ /* as evdns_request_len told you it should be. */ /* */ /* Returns the amount of space used. Negative on error. */ static int evdns_request_data_build(const char *const name, const size_t name_len, const u16 trans_id, const u16 type, const u16 class, u8 *const buf, size_t buf_len) { off_t j = 0; /* current offset into buf */ u16 t_; /* used by the macros */ APPEND16(trans_id); APPEND16(0x0100); /* standard query, recusion needed */ APPEND16(1); /* one question */ APPEND16(0); /* no answers */ APPEND16(0); /* no authority */ APPEND16(0); /* no additional */ j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL); if (j < 0) { return (int)j; } APPEND16(type); APPEND16(class); return (int)j; overflow: return (-1); } /* exported function */ struct evdns_server_port * evdns_add_server_port_with_base(struct event_base *base, evutil_socket_t socket, int flags, evdns_request_callback_fn_type cb, void *user_data) { struct evdns_server_port *port; if (flags) return NULL; /* flags not yet implemented */ if (!(port = mm_malloc(sizeof(struct evdns_server_port)))) return NULL; memset(port, 0, sizeof(struct evdns_server_port)); port->socket = socket; port->refcnt = 1; port->choked = 0; port->closing = 0; port->user_callback = cb; port->user_data = user_data; port->pending_replies = NULL; port->event_base = base; event_assign(&port->event, port->event_base, port->socket, EV_READ | EV_PERSIST, server_port_ready_callback, port); if (event_add(&port->event, NULL) < 0) { mm_free(port); return NULL; } EVTHREAD_ALLOC_LOCK(port->lock, EVTHREAD_LOCKTYPE_RECURSIVE); return port; } struct evdns_server_port * evdns_add_server_port(evutil_socket_t socket, int flags, evdns_request_callback_fn_type cb, void *user_data) { return evdns_add_server_port_with_base(NULL, socket, flags, cb, user_data); } /* exported function */ void evdns_close_server_port(struct evdns_server_port *port) { EVDNS_LOCK(port); if (--port->refcnt == 0) { EVDNS_UNLOCK(port); server_port_free(port); } else { port->closing = 1; EVDNS_UNLOCK(port); } } /* exported function */ int evdns_server_request_add_reply(struct evdns_server_request *req_, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data) { struct server_request *req = TO_SERVER_REQUEST(req_); struct server_reply_item **itemp, *item; int *countp; int result = -1; EVDNS_LOCK(req->port); if (req->response) /* have we already answered? */ goto done; switch (section) { case EVDNS_ANSWER_SECTION: itemp = &req->answer; countp = &req->n_answer; break; case EVDNS_AUTHORITY_SECTION: itemp = &req->authority; countp = &req->n_authority; break; case EVDNS_ADDITIONAL_SECTION: itemp = &req->additional; countp = &req->n_additional; break; default: goto done; } while (*itemp) { itemp = &((*itemp)->next); } item = mm_malloc(sizeof(struct server_reply_item)); if (!item) goto done; item->next = NULL; if (!(item->name = mm_strdup(name))) { mm_free(item); goto done; } item->type = type; item->dns_question_class = class; item->ttl = ttl; item->is_name = is_name != 0; item->datalen = 0; item->data = NULL; if (data) { if (item->is_name) { if (!(item->data = mm_strdup(data))) { mm_free(item->name); mm_free(item); goto done; } item->datalen = (u16)-1; } else { if (!(item->data = mm_malloc(datalen))) { mm_free(item->name); mm_free(item); goto done; } item->datalen = datalen; memcpy(item->data, data, datalen); } } *itemp = item; ++(*countp); result = 0; done: EVDNS_UNLOCK(req->port); return result; } /* exported function */ int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl) { return evdns_server_request_add_reply( req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, ttl, n*4, 0, addrs); } /* exported function */ int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl) { return evdns_server_request_add_reply( req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET, ttl, n*16, 0, addrs); } /* exported function */ int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl) { u32 a; char buf[32]; if (in && inaddr_name) return -1; else if (!in && !inaddr_name) return -1; if (in) { a = ntohl(in->s_addr); evutil_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", (int)(u8)((a )&0xff), (int)(u8)((a>>8 )&0xff), (int)(u8)((a>>16)&0xff), (int)(u8)((a>>24)&0xff)); inaddr_name = buf; } return evdns_server_request_add_reply( req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET, ttl, -1, 1, hostname); } /* exported function */ int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl) { return evdns_server_request_add_reply( req, EVDNS_ANSWER_SECTION, name, TYPE_CNAME, CLASS_INET, ttl, -1, 1, cname); } /* exported function */ void evdns_server_request_set_flags(struct evdns_server_request *exreq, int flags) { struct server_request *req = TO_SERVER_REQUEST(exreq); req->base.flags &= ~(EVDNS_FLAGS_AA|EVDNS_FLAGS_RD); req->base.flags |= flags; } static int evdns_server_request_format_response(struct server_request *req, int err) { unsigned char buf[1500]; size_t buf_len = sizeof(buf); off_t j = 0, r; u16 t_; u32 t32_; int i; u16 flags; struct dnslabel_table table; if (err < 0 || err > 15) return -1; /* Set response bit and error code; copy OPCODE and RD fields from * question; copy RA and AA if set by caller. */ flags = req->base.flags; flags |= (_QR_MASK | err); dnslabel_table_init(&table); APPEND16(req->trans_id); APPEND16(flags); APPEND16(req->base.nquestions); APPEND16(req->n_answer); APPEND16(req->n_authority); APPEND16(req->n_additional); /* Add questions. */ for (i=0; i < req->base.nquestions; ++i) { const char *s = req->base.questions[i]->name; j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table); if (j < 0) { dnslabel_clear(&table); return (int) j; } APPEND16(req->base.questions[i]->type); APPEND16(req->base.questions[i]->dns_question_class); } /* Add answer, authority, and additional sections. */ for (i=0; i<3; ++i) { struct server_reply_item *item; if (i==0) item = req->answer; else if (i==1) item = req->authority; else item = req->additional; while (item) { r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table); if (r < 0) goto overflow; j = r; APPEND16(item->type); APPEND16(item->dns_question_class); APPEND32(item->ttl); if (item->is_name) { off_t len_idx = j, name_start; j += 2; name_start = j; r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table); if (r < 0) goto overflow; j = r; t_ = htons( (short) (j-name_start) ); memcpy(buf+len_idx, &t_, 2); } else { APPEND16(item->datalen); if (j+item->datalen > (off_t)buf_len) goto overflow; memcpy(buf+j, item->data, item->datalen); j += item->datalen; } item = item->next; } } if (j > 512) { overflow: j = 512; buf[2] |= 0x02; /* set the truncated bit. */ } req->response_len = j; if (!(req->response = mm_malloc(req->response_len))) { server_request_free_answers(req); dnslabel_clear(&table); return (-1); } memcpy(req->response, buf, req->response_len); server_request_free_answers(req); dnslabel_clear(&table); return (0); } /* exported function */ int evdns_server_request_respond(struct evdns_server_request *req_, int err) { struct server_request *req = TO_SERVER_REQUEST(req_); struct evdns_server_port *port = req->port; int r = -1; EVDNS_LOCK(port); if (!req->response) { if ((r = evdns_server_request_format_response(req, err))<0) goto done; } r = sendto(port->socket, req->response, (int)req->response_len, 0, (struct sockaddr*) &req->addr, (ev_socklen_t)req->addrlen); if (r<0) { int sock_err = evutil_socket_geterror(port->socket); if (EVUTIL_ERR_RW_RETRIABLE(sock_err)) goto done; if (port->pending_replies) { req->prev_pending = port->pending_replies->prev_pending; req->next_pending = port->pending_replies; req->prev_pending->next_pending = req->next_pending->prev_pending = req; } else { req->prev_pending = req->next_pending = req; port->pending_replies = req; port->choked = 1; (void) event_del(&port->event); event_assign(&port->event, port->event_base, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port); if (event_add(&port->event, NULL) < 0) { log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server"); } } r = 1; goto done; } if (server_request_free(req)) { r = 0; goto done; } if (port->pending_replies) server_port_flush(port); r = 0; done: EVDNS_UNLOCK(port); return r; } /* Free all storage held by RRs in req. */ static void server_request_free_answers(struct server_request *req) { struct server_reply_item *victim, *next, **list; int i; for (i = 0; i < 3; ++i) { if (i==0) list = &req->answer; else if (i==1) list = &req->authority; else list = &req->additional; victim = *list; while (victim) { next = victim->next; mm_free(victim->name); if (victim->data) mm_free(victim->data); mm_free(victim); victim = next; } *list = NULL; } } /* Free all storage held by req, and remove links to it. */ /* return true iff we just wound up freeing the server_port. */ static int server_request_free(struct server_request *req) { int i, rc=1, lock=0; if (req->base.questions) { for (i = 0; i < req->base.nquestions; ++i) mm_free(req->base.questions[i]); mm_free(req->base.questions); } if (req->port) { EVDNS_LOCK(req->port); lock=1; if (req->port->pending_replies == req) { if (req->next_pending && req->next_pending != req) req->port->pending_replies = req->next_pending; else req->port->pending_replies = NULL; } rc = --req->port->refcnt; } if (req->response) { mm_free(req->response); } server_request_free_answers(req); if (req->next_pending && req->next_pending != req) { req->next_pending->prev_pending = req->prev_pending; req->prev_pending->next_pending = req->next_pending; } if (rc == 0) { EVDNS_UNLOCK(req->port); /* ????? nickm */ server_port_free(req->port); mm_free(req); return (1); } if (lock) EVDNS_UNLOCK(req->port); mm_free(req); return (0); } /* Free all storage held by an evdns_server_port. Only called when */ static void server_port_free(struct evdns_server_port *port) { EVUTIL_ASSERT(port); EVUTIL_ASSERT(!port->refcnt); EVUTIL_ASSERT(!port->pending_replies); if (port->socket > 0) { evutil_closesocket(port->socket); port->socket = -1; } (void) event_del(&port->event); event_debug_unassign(&port->event); EVTHREAD_FREE_LOCK(port->lock, EVTHREAD_LOCKTYPE_RECURSIVE); mm_free(port); } /* exported function */ int evdns_server_request_drop(struct evdns_server_request *req_) { struct server_request *req = TO_SERVER_REQUEST(req_); server_request_free(req); return 0; } /* exported function */ int evdns_server_request_get_requesting_addr(struct evdns_server_request *req_, struct sockaddr *sa, int addr_len) { struct server_request *req = TO_SERVER_REQUEST(req_); if (addr_len < (int)req->addrlen) return -1; memcpy(sa, &(req->addr), req->addrlen); return req->addrlen; } #undef APPEND16 #undef APPEND32 /* this is a libevent callback function which is called when a request */ /* has timed out. */ static void evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg) { struct request *const req = (struct request *) arg; struct evdns_base *base = req->base; (void) fd; (void) events; log(EVDNS_LOG_DEBUG, "Request %p timed out", arg); EVDNS_LOCK(base); if (req->tx_count >= req->base->global_max_retransmits) { struct nameserver *ns = req->ns; /* this request has failed */ log(EVDNS_LOG_DEBUG, "Giving up on request %p; tx_count==%d", arg, req->tx_count); reply_schedule_callback(req, 0, DNS_ERR_TIMEOUT, NULL); request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1); nameserver_failed(ns, "request timed out."); } else { /* retransmit it */ log(EVDNS_LOG_DEBUG, "Retransmitting request %p; tx_count==%d", arg, req->tx_count); (void) evtimer_del(&req->timeout_event); request_swap_ns(req, nameserver_pick(base)); evdns_request_transmit(req); req->ns->timedout++; if (req->ns->timedout > req->base->global_max_nameserver_timeout) { req->ns->timedout = 0; nameserver_failed(req->ns, "request timed out."); } } EVDNS_UNLOCK(base); } /* try to send a request to a given server. */ /* */ /* return: */ /* 0 ok */ /* 1 temporary failure */ /* 2 other failure */ static int evdns_request_transmit_to(struct request *req, struct nameserver *server) { int r; ASSERT_LOCKED(req->base); ASSERT_VALID_REQUEST(req); if (server->requests_inflight == 1 && req->base->disable_when_inactive && event_add(&server->event, NULL) < 0) { return 1; } r = sendto(server->socket, (void*)req->request, req->request_len, 0, (struct sockaddr *)&server->address, server->addrlen); if (r < 0) { int err = evutil_socket_geterror(server->socket); if (EVUTIL_ERR_RW_RETRIABLE(err)) return 1; nameserver_failed(req->ns, evutil_socket_error_to_string(err)); return 2; } else if (r != (int)req->request_len) { return 1; /* short write */ } else { return 0; } } /* try to send a request, updating the fields of the request */ /* as needed */ /* */ /* return: */ /* 0 ok */ /* 1 failed */ static int evdns_request_transmit(struct request *req) { int retcode = 0, r; ASSERT_LOCKED(req->base); ASSERT_VALID_REQUEST(req); /* if we fail to send this packet then this flag marks it */ /* for evdns_transmit */ req->transmit_me = 1; EVUTIL_ASSERT(req->trans_id != 0xffff); if (!req->ns) { /* unable to transmit request if no nameservers */ return 1; } if (req->ns->choked) { /* don't bother trying to write to a socket */ /* which we have had EAGAIN from */ return 1; } r = evdns_request_transmit_to(req, req->ns); switch (r) { case 1: /* temp failure */ req->ns->choked = 1; nameserver_write_waiting(req->ns, 1); return 1; case 2: /* failed to transmit the request entirely. we can fallthrough since * we'll set a timeout, which will time out, and make us retransmit the * request anyway. */ retcode = 1; EVUTIL_FALLTHROUGH; default: /* all ok */ log(EVDNS_LOG_DEBUG, "Setting timeout for request %p, sent to nameserver %p", req, req->ns); if (evtimer_add(&req->timeout_event, &req->base->global_timeout) < 0) { log(EVDNS_LOG_WARN, "Error from libevent when adding timer for request %p", req); /* ???? Do more? */ } req->tx_count++; req->transmit_me = 0; return retcode; } } static void nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) { struct nameserver *const ns = (struct nameserver *) arg; (void) type; (void) count; (void) ttl; (void) addresses; if (result == DNS_ERR_CANCEL) { /* We canceled this request because the nameserver came up * for some other reason. Do not change our opinion about * the nameserver. */ return; } EVDNS_LOCK(ns->base); ns->probe_request = NULL; if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) { /* this is a good reply */ nameserver_up(ns); } else { nameserver_probe_failed(ns); } EVDNS_UNLOCK(ns->base); } static void nameserver_send_probe(struct nameserver *const ns) { struct evdns_request *handle; struct request *req; char addrbuf[128]; /* here we need to send a probe to a given nameserver */ /* in the hope that it is up now. */ ASSERT_LOCKED(ns->base); log(EVDNS_LOG_DEBUG, "Sending probe to %s", evutil_format_sockaddr_port_( (struct sockaddr *)&ns->address, addrbuf, sizeof(addrbuf))); handle = mm_calloc(1, sizeof(*handle)); if (!handle) return; req = request_new(ns->base, handle, TYPE_A, "google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns); if (!req) { mm_free(handle); return; } ns->probe_request = handle; /* we force this into the inflight queue no matter what */ request_trans_id_set(req, transaction_id_pick(ns->base)); req->ns = ns; request_submit(req); } /* returns: */ /* 0 didn't try to transmit anything */ /* 1 tried to transmit something */ static int evdns_transmit(struct evdns_base *base) { char did_try_to_transmit = 0; int i; ASSERT_LOCKED(base); for (i = 0; i < base->n_req_heads; ++i) { if (base->req_heads[i]) { struct request *const started_at = base->req_heads[i], *req = started_at; /* first transmit all the requests which are currently waiting */ do { if (req->transmit_me) { did_try_to_transmit = 1; evdns_request_transmit(req); } req = req->next; } while (req != started_at); } } return did_try_to_transmit; } /* exported function */ int evdns_base_count_nameservers(struct evdns_base *base) { const struct nameserver *server; int n = 0; EVDNS_LOCK(base); server = base->server_head; if (!server) goto done; do { ++n; server = server->next; } while (server != base->server_head); done: EVDNS_UNLOCK(base); return n; } int evdns_count_nameservers(void) { return evdns_base_count_nameservers(current_base); } /* exported function */ int evdns_base_clear_nameservers_and_suspend(struct evdns_base *base) { struct nameserver *server, *started_at; int i; EVDNS_LOCK(base); server = base->server_head; started_at = base->server_head; if (!server) { EVDNS_UNLOCK(base); return 0; } while (1) { struct nameserver *next = server->next; (void) event_del(&server->event); if (evtimer_initialized(&server->timeout_event)) (void) evtimer_del(&server->timeout_event); if (server->probe_request) { evdns_cancel_request(server->base, server->probe_request); server->probe_request = NULL; } if (server->socket >= 0) evutil_closesocket(server->socket); mm_free(server); if (next == started_at) break; server = next; } base->server_head = NULL; base->global_good_nameservers = 0; for (i = 0; i < base->n_req_heads; ++i) { struct request *req, *req_started_at; req = req_started_at = base->req_heads[i]; while (req) { struct request *next = req->next; req->tx_count = req->reissue_count = 0; req->ns = NULL; /* ???? What to do about searches? */ (void) evtimer_del(&req->timeout_event); req->trans_id = 0; req->transmit_me = 0; base->global_requests_waiting++; evdns_request_insert(req, &base->req_waiting_head); /* We want to insert these suspended elements at the front of * the waiting queue, since they were pending before any of * the waiting entries were added. This is a circular list, * so we can just shift the start back by one.*/ base->req_waiting_head = base->req_waiting_head->prev; if (next == req_started_at) break; req = next; } base->req_heads[i] = NULL; } base->global_requests_inflight = 0; EVDNS_UNLOCK(base); return 0; } int evdns_clear_nameservers_and_suspend(void) { return evdns_base_clear_nameservers_and_suspend(current_base); } /* exported function */ int evdns_base_resume(struct evdns_base *base) { EVDNS_LOCK(base); evdns_requests_pump_waiting_queue(base); EVDNS_UNLOCK(base); return 0; } int evdns_resume(void) { return evdns_base_resume(current_base); } static int evdns_nameserver_add_impl_(struct evdns_base *base, const struct sockaddr *address, int addrlen) { /* first check to see if we already have this nameserver */ const struct nameserver *server = base->server_head, *const started_at = base->server_head; struct nameserver *ns; int err = 0; char addrbuf[128]; ASSERT_LOCKED(base); if (server) { do { if (!evutil_sockaddr_cmp((struct sockaddr*)&server->address, address, 1)) return 3; server = server->next; } while (server != started_at); } if (addrlen > (int)sizeof(ns->address)) { log(EVDNS_LOG_DEBUG, "Addrlen %d too long.", (int)addrlen); return 2; } ns = (struct nameserver *) mm_malloc(sizeof(struct nameserver)); if (!ns) return -1; memset(ns, 0, sizeof(struct nameserver)); ns->base = base; evtimer_assign(&ns->timeout_event, ns->base->event_base, nameserver_prod_callback, ns); ns->socket = evutil_socket_(address->sa_family, SOCK_DGRAM|EVUTIL_SOCK_NONBLOCK|EVUTIL_SOCK_CLOEXEC, 0); if (ns->socket < 0) { err = 1; goto out1; } if (base->global_outgoing_addrlen && !evutil_sockaddr_is_loopback_(address)) { if (bind(ns->socket, (struct sockaddr*)&base->global_outgoing_address, base->global_outgoing_addrlen) < 0) { log(EVDNS_LOG_WARN,"Couldn't bind to outgoing address"); err = 2; goto out2; } } if (base->so_rcvbuf) { if (setsockopt(ns->socket, SOL_SOCKET, SO_RCVBUF, (void *)&base->so_rcvbuf, sizeof(base->so_rcvbuf))) { log(EVDNS_LOG_WARN, "Couldn't set SO_RCVBUF to %i", base->so_rcvbuf); err = -SO_RCVBUF; goto out2; } } if (base->so_sndbuf) { if (setsockopt(ns->socket, SOL_SOCKET, SO_SNDBUF, (void *)&base->so_sndbuf, sizeof(base->so_sndbuf))) { log(EVDNS_LOG_WARN, "Couldn't set SO_SNDBUF to %i", base->so_sndbuf); err = -SO_SNDBUF; goto out2; } } memcpy(&ns->address, address, addrlen); ns->addrlen = addrlen; ns->state = 1; event_assign(&ns->event, ns->base->event_base, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns); if (!base->disable_when_inactive && event_add(&ns->event, NULL) < 0) { err = 2; goto out2; } log(EVDNS_LOG_DEBUG, "Added nameserver %s as %p", evutil_format_sockaddr_port_(address, addrbuf, sizeof(addrbuf)), ns); /* insert this nameserver into the list of them */ if (!base->server_head) { ns->next = ns->prev = ns; base->server_head = ns; } else { ns->next = base->server_head->next; ns->prev = base->server_head; base->server_head->next = ns; ns->next->prev = ns; } base->global_good_nameservers++; return 0; out2: evutil_closesocket(ns->socket); out1: event_debug_unassign(&ns->event); mm_free(ns); log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", evutil_format_sockaddr_port_(address, addrbuf, sizeof(addrbuf)), err); return err; } /* exported function */ int evdns_base_nameserver_add(struct evdns_base *base, unsigned long int address) { struct sockaddr_in sin; int res; memset(&sin, 0, sizeof(sin)); sin.sin_addr.s_addr = address; sin.sin_port = htons(53); sin.sin_family = AF_INET; EVDNS_LOCK(base); res = evdns_nameserver_add_impl_(base, (struct sockaddr*)&sin, sizeof(sin)); EVDNS_UNLOCK(base); return res; } int evdns_nameserver_add(unsigned long int address) { if (!current_base) current_base = evdns_base_new(NULL, 0); return evdns_base_nameserver_add(current_base, address); } static void sockaddr_setport(struct sockaddr *sa, ev_uint16_t port) { if (sa->sa_family == AF_INET) { ((struct sockaddr_in *)sa)->sin_port = htons(port); } else if (sa->sa_family == AF_INET6) { ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); } } static ev_uint16_t sockaddr_getport(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return ntohs(((struct sockaddr_in *)sa)->sin_port); } else if (sa->sa_family == AF_INET6) { return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); } else { return 0; } } /* exported function */ int evdns_base_nameserver_ip_add(struct evdns_base *base, const char *ip_as_string) { struct sockaddr_storage ss; struct sockaddr *sa; int len = sizeof(ss); int res; if (evutil_parse_sockaddr_port(ip_as_string, (struct sockaddr *)&ss, &len)) { log(EVDNS_LOG_WARN, "Unable to parse nameserver address %s", ip_as_string); return 4; } sa = (struct sockaddr *) &ss; if (sockaddr_getport(sa) == 0) sockaddr_setport(sa, 53); EVDNS_LOCK(base); res = evdns_nameserver_add_impl_(base, sa, len); EVDNS_UNLOCK(base); return res; } int evdns_nameserver_ip_add(const char *ip_as_string) { if (!current_base) current_base = evdns_base_new(NULL, 0); return evdns_base_nameserver_ip_add(current_base, ip_as_string); } int evdns_base_nameserver_sockaddr_add(struct evdns_base *base, const struct sockaddr *sa, ev_socklen_t len, unsigned flags) { int res; EVUTIL_ASSERT(base); EVDNS_LOCK(base); res = evdns_nameserver_add_impl_(base, sa, len); EVDNS_UNLOCK(base); return res; } int evdns_base_get_nameserver_addr(struct evdns_base *base, int idx, struct sockaddr *sa, ev_socklen_t len) { int result = -1; int i; struct nameserver *server; EVDNS_LOCK(base); server = base->server_head; for (i = 0; i < idx && server; ++i, server = server->next) { if (server->next == base->server_head) goto done; } if (! server) goto done; if (server->addrlen > len) { result = (int) server->addrlen; goto done; } memcpy(sa, &server->address, server->addrlen); result = (int) server->addrlen; done: EVDNS_UNLOCK(base); return result; } /* remove from the queue */ static void evdns_request_remove(struct request *req, struct request **head) { ASSERT_LOCKED(req->base); ASSERT_VALID_REQUEST(req); #if 0 { struct request *ptr; int found = 0; EVUTIL_ASSERT(*head != NULL); ptr = *head; do { if (ptr == req) { found = 1; break; } ptr = ptr->next; } while (ptr != *head); EVUTIL_ASSERT(found); EVUTIL_ASSERT(req->next); } #endif if (req->next == req) { /* only item in the list */ *head = NULL; } else { req->next->prev = req->prev; req->prev->next = req->next; if (*head == req) *head = req->next; } req->next = req->prev = NULL; } /* insert into the tail of the queue */ static void evdns_request_insert(struct request *req, struct request **head) { ASSERT_LOCKED(req->base); ASSERT_VALID_REQUEST(req); if (!*head) { *head = req; req->next = req->prev = req; return; } req->prev = (*head)->prev; req->prev->next = req; req->next = *head; (*head)->prev = req; } static int string_num_dots(const char *s) { int count = 0; while ((s = strchr(s, '.'))) { s++; count++; } return count; } static struct request * request_new(struct evdns_base *base, struct evdns_request *handle, int type, const char *name, int flags, evdns_callback_type callback, void *user_ptr) { const char issuing_now = (base->global_requests_inflight < base->global_max_requests_inflight) ? 1 : 0; const size_t name_len = strlen(name); const size_t request_max_len = evdns_request_len(name_len); const u16 trans_id = issuing_now ? transaction_id_pick(base) : 0xffff; /* the request data is alloced in a single block with the header */ struct request *const req = mm_malloc(sizeof(struct request) + request_max_len); int rlen; char namebuf[256]; (void) flags; ASSERT_LOCKED(base); if (!req) return NULL; if (name_len >= sizeof(namebuf)) { mm_free(req); return NULL; } memset(req, 0, sizeof(struct request)); req->base = base; evtimer_assign(&req->timeout_event, req->base->event_base, evdns_request_timeout_callback, req); if (base->global_randomize_case) { unsigned i; char randbits[(sizeof(namebuf)+7)/8]; strlcpy(namebuf, name, sizeof(namebuf)); evutil_secure_rng_get_bytes(randbits, (name_len+7)/8); for (i = 0; i < name_len; ++i) { if (EVUTIL_ISALPHA_(namebuf[i])) { if ((randbits[i >> 3] & (1<<(i & 7)))) namebuf[i] |= 0x20; else namebuf[i] &= ~0x20; } } name = namebuf; } /* request data lives just after the header */ req->request = ((u8 *) req) + sizeof(struct request); /* denotes that the request data shouldn't be free()ed */ req->request_appended = 1; rlen = evdns_request_data_build(name, name_len, trans_id, type, CLASS_INET, req->request, request_max_len); if (rlen < 0) goto err1; req->request_len = rlen; req->trans_id = trans_id; req->tx_count = 0; req->request_type = type; req->user_pointer = user_ptr; req->user_callback = callback; req->ns = issuing_now ? nameserver_pick(base) : NULL; req->next = req->prev = NULL; req->handle = handle; if (handle) { handle->current_req = req; handle->base = base; } return req; err1: mm_free(req); return NULL; } static void request_submit(struct request *const req) { struct evdns_base *base = req->base; ASSERT_LOCKED(base); ASSERT_VALID_REQUEST(req); if (req->ns) { /* if it has a nameserver assigned then this is going */ /* straight into the inflight queue */ evdns_request_insert(req, &REQ_HEAD(base, req->trans_id)); base->global_requests_inflight++; req->ns->requests_inflight++; evdns_request_transmit(req); } else { evdns_request_insert(req, &base->req_waiting_head); base->global_requests_waiting++; } } /* exported function */ void evdns_cancel_request(struct evdns_base *base, struct evdns_request *handle) { struct request *req; if (!handle->current_req) return; if (!base) { /* This redundancy is silly; can we fix it? (Not for 2.0) XXXX */ base = handle->base; if (!base) base = handle->current_req->base; } EVDNS_LOCK(base); if (handle->pending_cb) { EVDNS_UNLOCK(base); return; } req = handle->current_req; ASSERT_VALID_REQUEST(req); reply_schedule_callback(req, 0, DNS_ERR_CANCEL, NULL); if (req->ns) { /* remove from inflight queue */ request_finished(req, &REQ_HEAD(base, req->trans_id), 1); } else { /* remove from global_waiting head */ request_finished(req, &base->req_waiting_head, 1); } EVDNS_UNLOCK(base); } /* exported function */ struct evdns_request * evdns_base_resolve_ipv4(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr) { struct evdns_request *handle; struct request *req; log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); handle = mm_calloc(1, sizeof(*handle)); if (handle == NULL) return NULL; EVDNS_LOCK(base); if (flags & DNS_QUERY_NO_SEARCH) { req = request_new(base, handle, TYPE_A, name, flags, callback, ptr); if (req) request_submit(req); } else { search_request_new(base, handle, TYPE_A, name, flags, callback, ptr); } if (handle->current_req == NULL) { mm_free(handle); handle = NULL; } EVDNS_UNLOCK(base); return handle; } int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr) { return evdns_base_resolve_ipv4(current_base, name, flags, callback, ptr) ? 0 : -1; } /* exported function */ struct evdns_request * evdns_base_resolve_ipv6(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr) { struct evdns_request *handle; struct request *req; log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); handle = mm_calloc(1, sizeof(*handle)); if (handle == NULL) return NULL; EVDNS_LOCK(base); if (flags & DNS_QUERY_NO_SEARCH) { req = request_new(base, handle, TYPE_AAAA, name, flags, callback, ptr); if (req) request_submit(req); } else { search_request_new(base, handle, TYPE_AAAA, name, flags, callback, ptr); } if (handle->current_req == NULL) { mm_free(handle); handle = NULL; } EVDNS_UNLOCK(base); return handle; } int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr) { return evdns_base_resolve_ipv6(current_base, name, flags, callback, ptr) ? 0 : -1; } struct evdns_request * evdns_base_resolve_reverse(struct evdns_base *base, const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { char buf[32]; struct evdns_request *handle; struct request *req; u32 a; EVUTIL_ASSERT(in); a = ntohl(in->s_addr); evutil_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", (int)(u8)((a )&0xff), (int)(u8)((a>>8 )&0xff), (int)(u8)((a>>16)&0xff), (int)(u8)((a>>24)&0xff)); handle = mm_calloc(1, sizeof(*handle)); if (handle == NULL) return NULL; log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); EVDNS_LOCK(base); req = request_new(base, handle, TYPE_PTR, buf, flags, callback, ptr); if (req) request_submit(req); if (handle->current_req == NULL) { mm_free(handle); handle = NULL; } EVDNS_UNLOCK(base); return (handle); } int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { return evdns_base_resolve_reverse(current_base, in, flags, callback, ptr) ? 0 : -1; } struct evdns_request * evdns_base_resolve_reverse_ipv6(struct evdns_base *base, const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */ char buf[73]; char *cp; struct evdns_request *handle; struct request *req; int i; EVUTIL_ASSERT(in); cp = buf; for (i=15; i >= 0; --i) { u8 byte = in->s6_addr[i]; *cp++ = "0123456789abcdef"[byte & 0x0f]; *cp++ = '.'; *cp++ = "0123456789abcdef"[byte >> 4]; *cp++ = '.'; } EVUTIL_ASSERT(cp + strlen("ip6.arpa") < buf+sizeof(buf)); memcpy(cp, "ip6.arpa", strlen("ip6.arpa")+1); handle = mm_calloc(1, sizeof(*handle)); if (handle == NULL) return NULL; log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); EVDNS_LOCK(base); req = request_new(base, handle, TYPE_PTR, buf, flags, callback, ptr); if (req) request_submit(req); if (handle->current_req == NULL) { mm_free(handle); handle = NULL; } EVDNS_UNLOCK(base); return (handle); } int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { return evdns_base_resolve_reverse_ipv6(current_base, in, flags, callback, ptr) ? 0 : -1; } /* ================================================================= */ /* Search support */ /* */ /* the libc resolver has support for searching a number of domains */ /* to find a name. If nothing else then it takes the single domain */ /* from the gethostname() call. */ /* */ /* It can also be configured via the domain and search options in a */ /* resolv.conf. */ /* */ /* The ndots option controls how many dots it takes for the resolver */ /* to decide that a name is non-local and so try a raw lookup first. */ struct search_domain { int len; struct search_domain *next; /* the text string is appended to this structure */ }; struct search_state { int refcount; int ndots; int num_domains; struct search_domain *head; }; static void search_state_decref(struct search_state *const state) { if (!state) return; state->refcount--; if (!state->refcount) { struct search_domain *next, *dom; for (dom = state->head; dom; dom = next) { next = dom->next; mm_free(dom); } mm_free(state); } } static struct search_state * search_state_new(void) { struct search_state *state = (struct search_state *) mm_malloc(sizeof(struct search_state)); if (!state) return NULL; memset(state, 0, sizeof(struct search_state)); state->refcount = 1; state->ndots = 1; return state; } static void search_postfix_clear(struct evdns_base *base) { search_state_decref(base->global_search_state); base->global_search_state = search_state_new(); } /* exported function */ void evdns_base_search_clear(struct evdns_base *base) { EVDNS_LOCK(base); search_postfix_clear(base); EVDNS_UNLOCK(base); } void evdns_search_clear(void) { evdns_base_search_clear(current_base); } static void search_postfix_add(struct evdns_base *base, const char *domain) { size_t domain_len; struct search_domain *sdomain; while (domain[0] == '.') domain++; domain_len = strlen(domain); ASSERT_LOCKED(base); if (!base->global_search_state) base->global_search_state = search_state_new(); if (!base->global_search_state) return; base->global_search_state->num_domains++; sdomain = (struct search_domain *) mm_malloc(sizeof(struct search_domain) + domain_len); if (!sdomain) return; memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len); sdomain->next = base->global_search_state->head; sdomain->len = (int) domain_len; base->global_search_state->head = sdomain; } /* reverse the order of members in the postfix list. This is needed because, */ /* when parsing resolv.conf we push elements in the wrong order */ static void search_reverse(struct evdns_base *base) { struct search_domain *cur, *prev = NULL, *next; ASSERT_LOCKED(base); cur = base->global_search_state->head; while (cur) { next = cur->next; cur->next = prev; prev = cur; cur = next; } base->global_search_state->head = prev; } /* exported function */ void evdns_base_search_add(struct evdns_base *base, const char *domain) { EVDNS_LOCK(base); search_postfix_add(base, domain); EVDNS_UNLOCK(base); } void evdns_search_add(const char *domain) { evdns_base_search_add(current_base, domain); } /* exported function */ void evdns_base_search_ndots_set(struct evdns_base *base, const int ndots) { EVDNS_LOCK(base); if (!base->global_search_state) base->global_search_state = search_state_new(); if (base->global_search_state) base->global_search_state->ndots = ndots; EVDNS_UNLOCK(base); } void evdns_search_ndots_set(const int ndots) { evdns_base_search_ndots_set(current_base, ndots); } static void search_set_from_hostname(struct evdns_base *base) { char hostname[HOST_NAME_MAX + 1], *domainname; ASSERT_LOCKED(base); search_postfix_clear(base); if (gethostname(hostname, sizeof(hostname))) return; domainname = strchr(hostname, '.'); if (!domainname) return; search_postfix_add(base, domainname); } /* warning: returns malloced string */ static char * search_make_new(const struct search_state *const state, int n, const char *const base_name) { const size_t base_len = strlen(base_name); char need_to_append_dot; struct search_domain *dom; if (!base_len) return NULL; need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1; for (dom = state->head; dom; dom = dom->next) { if (!n--) { /* this is the postfix we want */ /* the actual postfix string is kept at the end of the structure */ const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain); const int postfix_len = dom->len; char *const newname = (char *) mm_malloc(base_len + need_to_append_dot + postfix_len + 1); if (!newname) return NULL; memcpy(newname, base_name, base_len); if (need_to_append_dot) newname[base_len] = '.'; memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len); newname[base_len + need_to_append_dot + postfix_len] = 0; return newname; } } /* we ran off the end of the list and still didn't find the requested string */ EVUTIL_ASSERT(0); return NULL; /* unreachable; stops warnings in some compilers. */ } static struct request * search_request_new(struct evdns_base *base, struct evdns_request *handle, int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) { ASSERT_LOCKED(base); EVUTIL_ASSERT(type == TYPE_A || type == TYPE_AAAA); EVUTIL_ASSERT(handle->current_req == NULL); if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) && base->global_search_state && base->global_search_state->num_domains) { /* we have some domains to search */ struct request *req; if (string_num_dots(name) >= base->global_search_state->ndots) { req = request_new(base, handle, type, name, flags, user_callback, user_arg); if (!req) return NULL; handle->search_index = -1; } else { char *const new_name = search_make_new(base->global_search_state, 0, name); if (!new_name) return NULL; req = request_new(base, handle, type, new_name, flags, user_callback, user_arg); mm_free(new_name); if (!req) return NULL; handle->search_index = 0; } EVUTIL_ASSERT(handle->search_origname == NULL); handle->search_origname = mm_strdup(name); if (handle->search_origname == NULL) { /* XXX Should we dealloc req? If yes, how? */ if (req) mm_free(req); return NULL; } handle->search_state = base->global_search_state; handle->search_flags = flags; base->global_search_state->refcount++; request_submit(req); return req; } else { struct request *const req = request_new(base, handle, type, name, flags, user_callback, user_arg); if (!req) return NULL; request_submit(req); return req; } } /* this is called when a request has failed to find a name. We need to check */ /* if it is part of a search and, if so, try the next name in the list */ /* returns: */ /* 0 another request has been submitted */ /* 1 no more requests needed */ static int search_try_next(struct evdns_request *const handle) { struct request *req = handle->current_req; struct evdns_base *base = req->base; struct request *newreq; ASSERT_LOCKED(base); if (handle->search_state) { /* it is part of a search */ char *new_name; handle->search_index++; if (handle->search_index >= handle->search_state->num_domains) { /* no more postfixes to try, however we may need to try */ /* this name without a postfix */ if (string_num_dots(handle->search_origname) < handle->search_state->ndots) { /* yep, we need to try it raw */ newreq = request_new(base, NULL, req->request_type, handle->search_origname, handle->search_flags, req->user_callback, req->user_pointer); log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", handle->search_origname); if (newreq) { search_request_finished(handle); goto submit_next; } } return 1; } new_name = search_make_new(handle->search_state, handle->search_index, handle->search_origname); if (!new_name) return 1; log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, handle->search_index); newreq = request_new(base, NULL, req->request_type, new_name, handle->search_flags, req->user_callback, req->user_pointer); mm_free(new_name); if (!newreq) return 1; goto submit_next; } return 1; submit_next: request_finished(req, &REQ_HEAD(req->base, req->trans_id), 0); handle->current_req = newreq; newreq->handle = handle; request_submit(newreq); return 0; } static void search_request_finished(struct evdns_request *const handle) { ASSERT_LOCKED(handle->current_req->base); if (handle->search_state) { search_state_decref(handle->search_state); handle->search_state = NULL; } if (handle->search_origname) { mm_free(handle->search_origname); handle->search_origname = NULL; } } /* ================================================================= */ /* Parsing resolv.conf files */ static void evdns_resolv_set_defaults(struct evdns_base *base, int flags) { int add_default = flags & DNS_OPTION_NAMESERVERS; if (flags & DNS_OPTION_NAMESERVERS_NO_DEFAULT) add_default = 0; /* if the file isn't found then we assume a local resolver */ ASSERT_LOCKED(base); if (flags & DNS_OPTION_SEARCH) search_set_from_hostname(base); if (add_default) evdns_base_nameserver_ip_add(base, "127.0.0.1"); } #ifndef EVENT__HAVE_STRTOK_R static char * strtok_r(char *s, const char *delim, char **state) { char *cp, *start; start = cp = s ? s : *state; if (!cp) return NULL; while (*cp && !strchr(delim, *cp)) ++cp; if (!*cp) { if (cp == start) return NULL; *state = NULL; return start; } else { *cp++ = '\0'; *state = cp; return start; } } #endif /* helper version of atoi which returns -1 on error */ static int strtoint(const char *const str) { char *endptr; const int r = strtol(str, &endptr, 10); if (*endptr) return -1; return r; } /* Parse a number of seconds into a timeval; return -1 on error. */ static int evdns_strtotimeval(const char *const str, struct timeval *out) { double d; char *endptr; d = strtod(str, &endptr); if (*endptr) return -1; if (d < 0) return -1; out->tv_sec = (int) d; out->tv_usec = (int) ((d - (int) d)*1000000); if (out->tv_sec == 0 && out->tv_usec < 1000) /* less than 1 msec */ return -1; return 0; } /* helper version of atoi that returns -1 on error and clips to bounds. */ static int strtoint_clipped(const char *const str, int min, int max) { int r = strtoint(str); if (r == -1) return r; else if (rmax) return max; else return r; } static int evdns_base_set_max_requests_inflight(struct evdns_base *base, int maxinflight) { int old_n_heads = base->n_req_heads, n_heads; struct request **old_heads = base->req_heads, **new_heads, *req; int i; ASSERT_LOCKED(base); if (maxinflight < 1) maxinflight = 1; n_heads = (maxinflight+4) / 5; EVUTIL_ASSERT(n_heads > 0); new_heads = mm_calloc(n_heads, sizeof(struct request*)); if (!new_heads) return (-1); if (old_heads) { for (i = 0; i < old_n_heads; ++i) { while (old_heads[i]) { req = old_heads[i]; evdns_request_remove(req, &old_heads[i]); evdns_request_insert(req, &new_heads[req->trans_id % n_heads]); } } mm_free(old_heads); } base->req_heads = new_heads; base->n_req_heads = n_heads; base->global_max_requests_inflight = maxinflight; return (0); } /* exported function */ int evdns_base_set_option(struct evdns_base *base, const char *option, const char *val) { int res; EVDNS_LOCK(base); res = evdns_base_set_option_impl(base, option, val, DNS_OPTIONS_ALL); EVDNS_UNLOCK(base); return res; } static inline int str_matches_option(const char *s1, const char *optionname) { /* Option names are given as "option:" We accept either 'option' in * s1, or 'option:randomjunk'. The latter form is to implement the * resolv.conf parser. */ size_t optlen = strlen(optionname); size_t slen = strlen(s1); if (slen == optlen || slen == optlen - 1) return !strncmp(s1, optionname, slen); else if (slen > optlen) return !strncmp(s1, optionname, optlen); else return 0; } static int evdns_base_set_option_impl(struct evdns_base *base, const char *option, const char *val, int flags) { ASSERT_LOCKED(base); if (str_matches_option(option, "ndots:")) { const int ndots = strtoint(val); if (ndots == -1) return -1; if (!(flags & DNS_OPTION_SEARCH)) return 0; log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots); if (!base->global_search_state) base->global_search_state = search_state_new(); if (!base->global_search_state) return -1; base->global_search_state->ndots = ndots; } else if (str_matches_option(option, "timeout:")) { struct timeval tv; if (evdns_strtotimeval(val, &tv) == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting timeout to %s", val); memcpy(&base->global_timeout, &tv, sizeof(struct timeval)); } else if (str_matches_option(option, "getaddrinfo-allow-skew:")) { struct timeval tv; if (evdns_strtotimeval(val, &tv) == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting getaddrinfo-allow-skew to %s", val); memcpy(&base->global_getaddrinfo_allow_skew, &tv, sizeof(struct timeval)); } else if (str_matches_option(option, "max-timeouts:")) { const int maxtimeout = strtoint_clipped(val, 1, 255); if (maxtimeout == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d", maxtimeout); base->global_max_nameserver_timeout = maxtimeout; } else if (str_matches_option(option, "max-inflight:")) { const int maxinflight = strtoint_clipped(val, 1, 65000); if (maxinflight == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d", maxinflight); evdns_base_set_max_requests_inflight(base, maxinflight); } else if (str_matches_option(option, "attempts:")) { int retries = strtoint(val); if (retries == -1) return -1; if (retries > 255) retries = 255; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries); base->global_max_retransmits = retries; } else if (str_matches_option(option, "randomize-case:")) { int randcase = strtoint(val); if (randcase == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; base->global_randomize_case = randcase; } else if (str_matches_option(option, "bind-to:")) { /* XXX This only applies to successive nameservers, not * to already-configured ones. We might want to fix that. */ int len = sizeof(base->global_outgoing_address); if (!(flags & DNS_OPTION_NAMESERVERS)) return 0; if (evutil_parse_sockaddr_port(val, (struct sockaddr*)&base->global_outgoing_address, &len)) return -1; base->global_outgoing_addrlen = len; } else if (str_matches_option(option, "initial-probe-timeout:")) { struct timeval tv; if (evdns_strtotimeval(val, &tv) == -1) return -1; if (tv.tv_sec > 3600) tv.tv_sec = 3600; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting initial probe timeout to %s", val); memcpy(&base->global_nameserver_probe_initial_timeout, &tv, sizeof(tv)); } else if (str_matches_option(option, "so-rcvbuf:")) { int buf = strtoint(val); if (buf == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting SO_RCVBUF to %s", val); base->so_rcvbuf = buf; } else if (str_matches_option(option, "so-sndbuf:")) { int buf = strtoint(val); if (buf == -1) return -1; if (!(flags & DNS_OPTION_MISC)) return 0; log(EVDNS_LOG_DEBUG, "Setting SO_SNDBUF to %s", val); base->so_sndbuf = buf; } return 0; } int evdns_set_option(const char *option, const char *val, int flags) { if (!current_base) current_base = evdns_base_new(NULL, 0); return evdns_base_set_option(current_base, option, val); } static void resolv_conf_parse_line(struct evdns_base *base, char *const start, int flags) { char *strtok_state; static const char *const delims = " \t"; #define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state) char *const first_token = strtok_r(start, delims, &strtok_state); ASSERT_LOCKED(base); if (!first_token) return; if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) { const char *const nameserver = NEXT_TOKEN; if (nameserver) evdns_base_nameserver_ip_add(base, nameserver); } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) { const char *const domain = NEXT_TOKEN; if (domain) { search_postfix_clear(base); search_postfix_add(base, domain); } } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) { const char *domain; search_postfix_clear(base); while ((domain = NEXT_TOKEN)) { search_postfix_add(base, domain); } search_reverse(base); } else if (!strcmp(first_token, "options")) { const char *option; while ((option = NEXT_TOKEN)) { const char *val = strchr(option, ':'); evdns_base_set_option_impl(base, option, val ? val+1 : "", flags); } } #undef NEXT_TOKEN } /* exported function */ /* returns: */ /* 0 no errors */ /* 1 failed to open file */ /* 2 failed to stat file */ /* 3 file too large */ /* 4 out of memory */ /* 5 short read from file */ int evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *const filename) { int res; EVDNS_LOCK(base); res = evdns_base_resolv_conf_parse_impl(base, flags, filename); EVDNS_UNLOCK(base); return res; } static char * evdns_get_default_hosts_filename(void) { #ifdef _WIN32 /* Windows is a little coy about where it puts its configuration * files. Sure, they're _usually_ in C:\windows\system32, but * there's no reason in principle they couldn't be in * W:\hoboken chicken emergency\ */ char path[MAX_PATH+1]; static const char hostfile[] = "\\drivers\\etc\\hosts"; char *path_out; size_t len_out; if (! SHGetSpecialFolderPathA(NULL, path, CSIDL_SYSTEM, 0)) return NULL; len_out = strlen(path)+strlen(hostfile)+1; path_out = mm_malloc(len_out); evutil_snprintf(path_out, len_out, "%s%s", path, hostfile); return path_out; #else return mm_strdup("/etc/hosts"); #endif } static int evdns_base_resolv_conf_parse_impl(struct evdns_base *base, int flags, const char *const filename) { size_t n; char *resolv; char *start; int err = 0; int add_default; log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); add_default = flags & DNS_OPTION_NAMESERVERS; if (flags & DNS_OPTION_NAMESERVERS_NO_DEFAULT) add_default = 0; if (flags & DNS_OPTION_HOSTSFILE) { char *fname = evdns_get_default_hosts_filename(); evdns_base_load_hosts(base, fname); if (fname) mm_free(fname); } if (!filename) { evdns_resolv_set_defaults(base, flags); return 1; } if ((err = evutil_read_file_(filename, &resolv, &n, 0)) < 0) { if (err == -1) { /* No file. */ evdns_resolv_set_defaults(base, flags); return 1; } else { return 2; } } start = resolv; for (;;) { char *const newline = strchr(start, '\n'); if (!newline) { resolv_conf_parse_line(base, start, flags); break; } else { *newline = 0; resolv_conf_parse_line(base, start, flags); start = newline + 1; } } if (!base->server_head && add_default) { /* no nameservers were configured. */ evdns_base_nameserver_ip_add(base, "127.0.0.1"); err = 6; } if (flags & DNS_OPTION_SEARCH && (!base->global_search_state || base->global_search_state->num_domains == 0)) { search_set_from_hostname(base); } mm_free(resolv); return err; } int evdns_resolv_conf_parse(int flags, const char *const filename) { if (!current_base) current_base = evdns_base_new(NULL, 0); return evdns_base_resolv_conf_parse(current_base, flags, filename); } #ifdef _WIN32 /* Add multiple nameservers from a space-or-comma-separated list. */ static int evdns_nameserver_ip_add_line(struct evdns_base *base, const char *ips) { const char *addr; char *buf; int r; ASSERT_LOCKED(base); while (*ips) { while (isspace(*ips) || *ips == ',' || *ips == '\t') ++ips; addr = ips; while (isdigit(*ips) || *ips == '.' || *ips == ':' || *ips=='[' || *ips==']') ++ips; buf = mm_malloc(ips-addr+1); if (!buf) return 4; memcpy(buf, addr, ips-addr); buf[ips-addr] = '\0'; r = evdns_base_nameserver_ip_add(base, buf); mm_free(buf); if (r) return r; } return 0; } typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*); /* Use the windows GetNetworkParams interface in iphlpapi.dll to */ /* figure out what our nameservers are. */ static int load_nameservers_with_getnetworkparams(struct evdns_base *base) { /* Based on MSDN examples and inspection of c-ares code. */ FIXED_INFO *fixed; HMODULE handle = 0; ULONG size = sizeof(FIXED_INFO); void *buf = NULL; int status = 0, r, added_any; IP_ADDR_STRING *ns; GetNetworkParams_fn_t fn; ASSERT_LOCKED(base); if (!(handle = evutil_load_windows_system_library_( TEXT("iphlpapi.dll")))) { log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll"); status = -1; goto done; } if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) { log(EVDNS_LOG_WARN, "Could not get address of function."); status = -1; goto done; } buf = mm_malloc(size); if (!buf) { status = 4; goto done; } fixed = buf; r = fn(fixed, &size); if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) { status = -1; goto done; } if (r != ERROR_SUCCESS) { mm_free(buf); buf = mm_malloc(size); if (!buf) { status = 4; goto done; } fixed = buf; r = fn(fixed, &size); if (r != ERROR_SUCCESS) { log(EVDNS_LOG_DEBUG, "fn() failed."); status = -1; goto done; } } EVUTIL_ASSERT(fixed); added_any = 0; ns = &(fixed->DnsServerList); while (ns) { r = evdns_nameserver_ip_add_line(base, ns->IpAddress.String); if (r) { log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d", (ns->IpAddress.String),(int)GetLastError()); status = r; } else { ++added_any; log(EVDNS_LOG_DEBUG,"Successfully added %s as nameserver",ns->IpAddress.String); } ns = ns->Next; } if (!added_any) { log(EVDNS_LOG_DEBUG, "No nameservers added."); if (status == 0) status = -1; } else { status = 0; } done: if (buf) mm_free(buf); if (handle) FreeLibrary(handle); return status; } static int config_nameserver_from_reg_key(struct evdns_base *base, HKEY key, const TCHAR *subkey) { char *buf; DWORD bufsz = 0, type = 0; int status = 0; ASSERT_LOCKED(base); if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz) != ERROR_MORE_DATA) return -1; if (!(buf = mm_malloc(bufsz))) return -1; if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz) == ERROR_SUCCESS && bufsz > 1) { status = evdns_nameserver_ip_add_line(base,buf); } mm_free(buf); return status; } #define SERVICES_KEY TEXT("System\\CurrentControlSet\\Services\\") #define WIN_NS_9X_KEY SERVICES_KEY TEXT("VxD\\MSTCP") #define WIN_NS_NT_KEY SERVICES_KEY TEXT("Tcpip\\Parameters") static int load_nameservers_from_registry(struct evdns_base *base) { int found = 0; int r; #define TRY(k, name) \ if (!found && config_nameserver_from_reg_key(base,k,TEXT(name)) == 0) { \ log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \ found = 1; \ } else if (!found) { \ log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \ #k,#name); \ } ASSERT_LOCKED(base); if (((int)GetVersion()) > 0) { /* NT */ HKEY nt_key = 0, interfaces_key = 0; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &nt_key) != ERROR_SUCCESS) { log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError()); return -1; } r = RegOpenKeyEx(nt_key, TEXT("Interfaces"), 0, KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &interfaces_key); if (r != ERROR_SUCCESS) { log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError()); return -1; } TRY(nt_key, "NameServer"); TRY(nt_key, "DhcpNameServer"); TRY(interfaces_key, "NameServer"); TRY(interfaces_key, "DhcpNameServer"); RegCloseKey(interfaces_key); RegCloseKey(nt_key); } else { HKEY win_key = 0; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0, KEY_READ, &win_key) != ERROR_SUCCESS) { log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError()); return -1; } TRY(win_key, "NameServer"); RegCloseKey(win_key); } if (found == 0) { log(EVDNS_LOG_WARN,"Didn't find any nameservers."); } return found ? 0 : -1; #undef TRY } int evdns_base_config_windows_nameservers(struct evdns_base *base) { int r; char *fname; if (base == NULL) base = current_base; if (base == NULL) return -1; EVDNS_LOCK(base); fname = evdns_get_default_hosts_filename(); log(EVDNS_LOG_DEBUG, "Loading hosts entries from %s", fname); evdns_base_load_hosts(base, fname); if (fname) mm_free(fname); if (load_nameservers_with_getnetworkparams(base) == 0) { EVDNS_UNLOCK(base); return 0; } r = load_nameservers_from_registry(base); EVDNS_UNLOCK(base); return r; } int evdns_config_windows_nameservers(void) { if (!current_base) { current_base = evdns_base_new(NULL, 1); return current_base == NULL ? -1 : 0; } else { return evdns_base_config_windows_nameservers(current_base); } } #endif struct evdns_base * evdns_base_new(struct event_base *event_base, int flags) { struct evdns_base *base; if (evutil_secure_rng_init() < 0) { log(EVDNS_LOG_WARN, "Unable to seed random number generator; " "DNS can't run."); return NULL; } /* Give the evutil library a hook into its evdns-enabled * functionality. We can't just call evdns_getaddrinfo directly or * else libevent-core will depend on libevent-extras. */ evutil_set_evdns_getaddrinfo_fn_(evdns_getaddrinfo); evutil_set_evdns_getaddrinfo_cancel_fn_(evdns_getaddrinfo_cancel); base = mm_malloc(sizeof(struct evdns_base)); if (base == NULL) return (NULL); memset(base, 0, sizeof(struct evdns_base)); base->req_waiting_head = NULL; EVTHREAD_ALLOC_LOCK(base->lock, EVTHREAD_LOCKTYPE_RECURSIVE); EVDNS_LOCK(base); /* Set max requests inflight and allocate req_heads. */ base->req_heads = NULL; evdns_base_set_max_requests_inflight(base, 64); base->server_head = NULL; base->event_base = event_base; base->global_good_nameservers = base->global_requests_inflight = base->global_requests_waiting = 0; base->global_timeout.tv_sec = 5; base->global_timeout.tv_usec = 0; base->global_max_reissues = 1; base->global_max_retransmits = 3; base->global_max_nameserver_timeout = 3; base->global_search_state = NULL; base->global_randomize_case = 1; base->global_getaddrinfo_allow_skew.tv_sec = 3; base->global_getaddrinfo_allow_skew.tv_usec = 0; base->global_nameserver_probe_initial_timeout.tv_sec = 10; base->global_nameserver_probe_initial_timeout.tv_usec = 0; TAILQ_INIT(&base->hostsdb); #define EVDNS_BASE_ALL_FLAGS ( \ EVDNS_BASE_INITIALIZE_NAMESERVERS | \ EVDNS_BASE_DISABLE_WHEN_INACTIVE | \ EVDNS_BASE_NAMESERVERS_NO_DEFAULT | \ 0) if (flags & ~EVDNS_BASE_ALL_FLAGS) { flags = EVDNS_BASE_INITIALIZE_NAMESERVERS; log(EVDNS_LOG_WARN, "Unrecognized flag passed to evdns_base_new(). Assuming " "you meant EVDNS_BASE_INITIALIZE_NAMESERVERS."); } #undef EVDNS_BASE_ALL_FLAGS if (flags & EVDNS_BASE_INITIALIZE_NAMESERVERS) { int r; int opts = DNS_OPTIONS_ALL; if (flags & EVDNS_BASE_NAMESERVERS_NO_DEFAULT) { opts |= DNS_OPTION_NAMESERVERS_NO_DEFAULT; } #ifdef _WIN32 r = evdns_base_config_windows_nameservers(base); #else r = evdns_base_resolv_conf_parse(base, opts, "/etc/resolv.conf"); #endif if (r) { evdns_base_free_and_unlock(base, 0); return NULL; } } if (flags & EVDNS_BASE_DISABLE_WHEN_INACTIVE) { base->disable_when_inactive = 1; } EVDNS_UNLOCK(base); return base; } int evdns_init(void) { struct evdns_base *base = evdns_base_new(NULL, 1); if (base) { current_base = base; return 0; } else { return -1; } } const char * evdns_err_to_string(int err) { switch (err) { case DNS_ERR_NONE: return "no error"; case DNS_ERR_FORMAT: return "misformatted query"; case DNS_ERR_SERVERFAILED: return "server failed"; case DNS_ERR_NOTEXIST: return "name does not exist"; case DNS_ERR_NOTIMPL: return "query not implemented"; case DNS_ERR_REFUSED: return "refused"; case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed"; case DNS_ERR_UNKNOWN: return "unknown"; case DNS_ERR_TIMEOUT: return "request timed out"; case DNS_ERR_SHUTDOWN: return "dns subsystem shut down"; case DNS_ERR_CANCEL: return "dns request canceled"; case DNS_ERR_NODATA: return "no records in the reply"; default: return "[Unknown error code]"; } } static void evdns_nameserver_free(struct nameserver *server) { if (server->socket >= 0) evutil_closesocket(server->socket); (void) event_del(&server->event); event_debug_unassign(&server->event); if (server->state == 0) (void) event_del(&server->timeout_event); if (server->probe_request) { evdns_cancel_request(server->base, server->probe_request); server->probe_request = NULL; } event_debug_unassign(&server->timeout_event); mm_free(server); } static void evdns_base_free_and_unlock(struct evdns_base *base, int fail_requests) { struct nameserver *server, *server_next; struct search_domain *dom, *dom_next; int i; /* Requires that we hold the lock. */ /* TODO(nickm) we might need to refcount here. */ while (base->req_waiting_head) { if (fail_requests) reply_schedule_callback(base->req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL); request_finished(base->req_waiting_head, &base->req_waiting_head, 1); } for (i = 0; i < base->n_req_heads; ++i) { while (base->req_heads[i]) { if (fail_requests) reply_schedule_callback(base->req_heads[i], 0, DNS_ERR_SHUTDOWN, NULL); request_finished(base->req_heads[i], &REQ_HEAD(base, base->req_heads[i]->trans_id), 1); } } base->global_requests_inflight = base->global_requests_waiting = 0; for (server = base->server_head; server; server = server_next) { server_next = server->next; /** already done something before */ server->probe_request = NULL; evdns_nameserver_free(server); if (server_next == base->server_head) break; } base->server_head = NULL; base->global_good_nameservers = 0; if (base->global_search_state) { for (dom = base->global_search_state->head; dom; dom = dom_next) { dom_next = dom->next; mm_free(dom); } mm_free(base->global_search_state); base->global_search_state = NULL; } { struct hosts_entry *victim; while ((victim = TAILQ_FIRST(&base->hostsdb))) { TAILQ_REMOVE(&base->hostsdb, victim, next); mm_free(victim); } } mm_free(base->req_heads); EVDNS_UNLOCK(base); EVTHREAD_FREE_LOCK(base->lock, EVTHREAD_LOCKTYPE_RECURSIVE); mm_free(base); } void evdns_base_free(struct evdns_base *base, int fail_requests) { EVDNS_LOCK(base); evdns_base_free_and_unlock(base, fail_requests); } void evdns_base_clear_host_addresses(struct evdns_base *base) { struct hosts_entry *victim; EVDNS_LOCK(base); while ((victim = TAILQ_FIRST(&base->hostsdb))) { TAILQ_REMOVE(&base->hostsdb, victim, next); mm_free(victim); } EVDNS_UNLOCK(base); } void evdns_shutdown(int fail_requests) { if (current_base) { struct evdns_base *b = current_base; current_base = NULL; evdns_base_free(b, fail_requests); } evdns_log_fn = NULL; } static int evdns_base_parse_hosts_line(struct evdns_base *base, char *line) { char *strtok_state; static const char *const delims = " \t"; char *const addr = strtok_r(line, delims, &strtok_state); char *hostname, *hash; struct sockaddr_storage ss; int socklen = sizeof(ss); ASSERT_LOCKED(base); #define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state) if (!addr || *addr == '#') return 0; memset(&ss, 0, sizeof(ss)); if (evutil_parse_sockaddr_port(addr, (struct sockaddr*)&ss, &socklen)<0) return -1; if (socklen > (int)sizeof(struct sockaddr_in6)) return -1; if (sockaddr_getport((struct sockaddr*)&ss)) return -1; while ((hostname = NEXT_TOKEN)) { struct hosts_entry *he; size_t namelen; if ((hash = strchr(hostname, '#'))) { if (hash == hostname) return 0; *hash = '\0'; } namelen = strlen(hostname); he = mm_calloc(1, sizeof(struct hosts_entry)+namelen); if (!he) return -1; EVUTIL_ASSERT(socklen <= (int)sizeof(he->addr)); memcpy(&he->addr, &ss, socklen); memcpy(he->hostname, hostname, namelen+1); he->addrlen = socklen; TAILQ_INSERT_TAIL(&base->hostsdb, he, next); if (hash) return 0; } return 0; #undef NEXT_TOKEN } static int evdns_base_load_hosts_impl(struct evdns_base *base, const char *hosts_fname) { char *str=NULL, *cp, *eol; size_t len; int err=0; ASSERT_LOCKED(base); if (hosts_fname == NULL || (err = evutil_read_file_(hosts_fname, &str, &len, 0)) < 0) { char tmp[64]; strlcpy(tmp, "127.0.0.1 localhost", sizeof(tmp)); evdns_base_parse_hosts_line(base, tmp); strlcpy(tmp, "::1 localhost", sizeof(tmp)); evdns_base_parse_hosts_line(base, tmp); return err ? -1 : 0; } /* This will break early if there is a NUL in the hosts file. * Probably not a problem.*/ cp = str; for (;;) { eol = strchr(cp, '\n'); if (eol) { *eol = '\0'; evdns_base_parse_hosts_line(base, cp); cp = eol+1; } else { evdns_base_parse_hosts_line(base, cp); break; } } mm_free(str); return 0; } int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname) { int res; if (!base) base = current_base; EVDNS_LOCK(base); res = evdns_base_load_hosts_impl(base, hosts_fname); EVDNS_UNLOCK(base); return res; } /* A single request for a getaddrinfo, either v4 or v6. */ struct getaddrinfo_subrequest { struct evdns_request *r; ev_uint32_t type; }; /* State data used to implement an in-progress getaddrinfo. */ struct evdns_getaddrinfo_request { struct evdns_base *evdns_base; /* Copy of the modified 'hints' data that we'll use to build * answers. */ struct evutil_addrinfo hints; /* The callback to invoke when we're done */ evdns_getaddrinfo_cb user_cb; /* User-supplied data to give to the callback. */ void *user_data; /* The port to use when building sockaddrs. */ ev_uint16_t port; /* The sub_request for an A record (if any) */ struct getaddrinfo_subrequest ipv4_request; /* The sub_request for an AAAA record (if any) */ struct getaddrinfo_subrequest ipv6_request; /* The cname result that we were told (if any) */ char *cname_result; /* If we have one request answered and one request still inflight, * then this field holds the answer from the first request... */ struct evutil_addrinfo *pending_result; /* And this event is a timeout that will tell us to cancel the second * request if it's taking a long time. */ struct event timeout; /* And this field holds the error code from the first request... */ int pending_error; /* If this is set, the user canceled this request. */ unsigned user_canceled : 1; /* If this is set, the user can no longer cancel this request; we're * just waiting for the free. */ unsigned request_done : 1; }; /* Convert an evdns errors to the equivalent getaddrinfo error. */ static int evdns_err_to_getaddrinfo_err(int e1) { /* XXX Do this better! */ if (e1 == DNS_ERR_NONE) return 0; else if (e1 == DNS_ERR_NOTEXIST) return EVUTIL_EAI_NONAME; else return EVUTIL_EAI_FAIL; } /* Return the more informative of two getaddrinfo errors. */ static int getaddrinfo_merge_err(int e1, int e2) { /* XXXX be cleverer here. */ if (e1 == 0) return e2; else return e1; } static void free_getaddrinfo_request(struct evdns_getaddrinfo_request *data) { /* DO NOT CALL this if either of the requests is pending. Only once * both callbacks have been invoked is it safe to free the request */ if (data->pending_result) evutil_freeaddrinfo(data->pending_result); if (data->cname_result) mm_free(data->cname_result); event_del(&data->timeout); mm_free(data); return; } static void add_cname_to_reply(struct evdns_getaddrinfo_request *data, struct evutil_addrinfo *ai) { if (data->cname_result && ai) { ai->ai_canonname = data->cname_result; data->cname_result = NULL; } } /* Callback: invoked when one request in a mixed-format A/AAAA getaddrinfo * request has finished, but the other one took too long to answer. Pass * along the answer we got, and cancel the other request. */ static void evdns_getaddrinfo_timeout_cb(evutil_socket_t fd, short what, void *ptr) { int v4_timedout = 0, v6_timedout = 0; struct evdns_getaddrinfo_request *data = ptr; /* Cancel any pending requests, and note which one */ if (data->ipv4_request.r) { /* XXXX This does nothing if the request's callback is already * running (pending_cb is set). */ evdns_cancel_request(NULL, data->ipv4_request.r); v4_timedout = 1; EVDNS_LOCK(data->evdns_base); ++data->evdns_base->getaddrinfo_ipv4_timeouts; EVDNS_UNLOCK(data->evdns_base); } if (data->ipv6_request.r) { /* XXXX This does nothing if the request's callback is already * running (pending_cb is set). */ evdns_cancel_request(NULL, data->ipv6_request.r); v6_timedout = 1; EVDNS_LOCK(data->evdns_base); ++data->evdns_base->getaddrinfo_ipv6_timeouts; EVDNS_UNLOCK(data->evdns_base); } /* We only use this timeout callback when we have an answer for * one address. */ EVUTIL_ASSERT(!v4_timedout || !v6_timedout); /* Report the outcome of the other request that didn't time out. */ if (data->pending_result) { add_cname_to_reply(data, data->pending_result); data->user_cb(0, data->pending_result, data->user_data); data->pending_result = NULL; } else { int e = data->pending_error; if (!e) e = EVUTIL_EAI_AGAIN; data->user_cb(e, NULL, data->user_data); } data->user_cb = NULL; /* prevent double-call if evdns callbacks are * in-progress. XXXX It would be better if this * weren't necessary. */ if (!v4_timedout && !v6_timedout) { /* should be impossible? XXXX */ free_getaddrinfo_request(data); } } static int evdns_getaddrinfo_set_timeout(struct evdns_base *evdns_base, struct evdns_getaddrinfo_request *data) { return event_add(&data->timeout, &evdns_base->global_getaddrinfo_allow_skew); } static inline int evdns_result_is_answer(int result) { return (result != DNS_ERR_NOTIMPL && result != DNS_ERR_REFUSED && result != DNS_ERR_SERVERFAILED && result != DNS_ERR_CANCEL); } static void evdns_getaddrinfo_gotresolve(int result, char type, int count, int ttl, void *addresses, void *arg) { int i; struct getaddrinfo_subrequest *req = arg; struct getaddrinfo_subrequest *other_req; struct evdns_getaddrinfo_request *data; struct evutil_addrinfo *res; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr *sa; int socklen, addrlen; void *addrp; int err; int user_canceled; EVUTIL_ASSERT(req->type == DNS_IPv4_A || req->type == DNS_IPv6_AAAA); if (req->type == DNS_IPv4_A) { data = EVUTIL_UPCAST(req, struct evdns_getaddrinfo_request, ipv4_request); other_req = &data->ipv6_request; } else { data = EVUTIL_UPCAST(req, struct evdns_getaddrinfo_request, ipv6_request); other_req = &data->ipv4_request; } /** Called from evdns_base_free() with @fail_requests == 1 */ if (result != DNS_ERR_SHUTDOWN) { EVDNS_LOCK(data->evdns_base); if (evdns_result_is_answer(result)) { if (req->type == DNS_IPv4_A) ++data->evdns_base->getaddrinfo_ipv4_answered; else ++data->evdns_base->getaddrinfo_ipv6_answered; } user_canceled = data->user_canceled; if (other_req->r == NULL) data->request_done = 1; EVDNS_UNLOCK(data->evdns_base); } else { data->evdns_base = NULL; user_canceled = data->user_canceled; } req->r = NULL; if (result == DNS_ERR_CANCEL && ! user_canceled) { /* Internal cancel request from timeout or internal error. * we already answered the user. */ if (other_req->r == NULL) free_getaddrinfo_request(data); return; } if (data->user_cb == NULL) { /* We already answered. XXXX This shouldn't be needed; see * comments in evdns_getaddrinfo_timeout_cb */ free_getaddrinfo_request(data); return; } if (result == DNS_ERR_NONE) { if (count == 0) err = EVUTIL_EAI_NODATA; else err = 0; } else { err = evdns_err_to_getaddrinfo_err(result); } if (err) { /* Looks like we got an error. */ if (other_req->r) { /* The other request is still working; maybe it will * succeed. */ /* XXXX handle failure from set_timeout */ if (result != DNS_ERR_SHUTDOWN) { evdns_getaddrinfo_set_timeout(data->evdns_base, data); } data->pending_error = err; return; } if (user_canceled) { data->user_cb(EVUTIL_EAI_CANCEL, NULL, data->user_data); } else if (data->pending_result) { /* If we have an answer waiting, and we weren't * canceled, ignore this error. */ add_cname_to_reply(data, data->pending_result); data->user_cb(0, data->pending_result, data->user_data); data->pending_result = NULL; } else { if (data->pending_error) err = getaddrinfo_merge_err(err, data->pending_error); data->user_cb(err, NULL, data->user_data); } free_getaddrinfo_request(data); return; } else if (user_canceled) { if (other_req->r) { /* The other request is still working; let it hit this * callback with EVUTIL_EAI_CANCEL callback and report * the failure. */ return; } data->user_cb(EVUTIL_EAI_CANCEL, NULL, data->user_data); free_getaddrinfo_request(data); return; } /* Looks like we got some answers. We should turn them into addrinfos * and then either queue those or return them all. */ EVUTIL_ASSERT(type == DNS_IPv4_A || type == DNS_IPv6_AAAA); if (type == DNS_IPv4_A) { memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(data->port); sa = (struct sockaddr *)&sin; socklen = sizeof(sin); addrlen = 4; addrp = &sin.sin_addr.s_addr; } else { memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(data->port); sa = (struct sockaddr *)&sin6; socklen = sizeof(sin6); addrlen = 16; addrp = &sin6.sin6_addr.s6_addr; } res = NULL; for (i=0; i < count; ++i) { struct evutil_addrinfo *ai; memcpy(addrp, ((char*)addresses)+i*addrlen, addrlen); ai = evutil_new_addrinfo_(sa, socklen, &data->hints); if (!ai) { if (other_req->r) { evdns_cancel_request(NULL, other_req->r); } data->user_cb(EVUTIL_EAI_MEMORY, NULL, data->user_data); if (res) evutil_freeaddrinfo(res); if (other_req->r == NULL) free_getaddrinfo_request(data); return; } res = evutil_addrinfo_append_(res, ai); } if (other_req->r) { /* The other request is still in progress; wait for it */ /* XXXX handle failure from set_timeout */ evdns_getaddrinfo_set_timeout(data->evdns_base, data); data->pending_result = res; return; } else { /* The other request is done or never started; append its * results (if any) and return them. */ if (data->pending_result) { if (req->type == DNS_IPv4_A) res = evutil_addrinfo_append_(res, data->pending_result); else res = evutil_addrinfo_append_( data->pending_result, res); data->pending_result = NULL; } /* Call the user callback. */ add_cname_to_reply(data, res); data->user_cb(0, res, data->user_data); /* Free data. */ free_getaddrinfo_request(data); } } static struct hosts_entry * find_hosts_entry(struct evdns_base *base, const char *hostname, struct hosts_entry *find_after) { struct hosts_entry *e; if (find_after) e = TAILQ_NEXT(find_after, next); else e = TAILQ_FIRST(&base->hostsdb); for (; e; e = TAILQ_NEXT(e, next)) { if (!evutil_ascii_strcasecmp(e->hostname, hostname)) return e; } return NULL; } static int evdns_getaddrinfo_fromhosts(struct evdns_base *base, const char *nodename, struct evutil_addrinfo *hints, ev_uint16_t port, struct evutil_addrinfo **res) { int n_found = 0; struct hosts_entry *e; struct evutil_addrinfo *ai=NULL; int f = hints->ai_family; EVDNS_LOCK(base); for (e = find_hosts_entry(base, nodename, NULL); e; e = find_hosts_entry(base, nodename, e)) { struct evutil_addrinfo *ai_new; ++n_found; if ((e->addr.sa.sa_family == AF_INET && f == PF_INET6) || (e->addr.sa.sa_family == AF_INET6 && f == PF_INET)) continue; ai_new = evutil_new_addrinfo_(&e->addr.sa, e->addrlen, hints); if (!ai_new) { n_found = 0; goto out; } sockaddr_setport(ai_new->ai_addr, port); ai = evutil_addrinfo_append_(ai, ai_new); } EVDNS_UNLOCK(base); out: if (n_found) { /* Note that we return an empty answer if we found entries for * this hostname but none were of the right address type. */ *res = ai; return 0; } else { if (ai) evutil_freeaddrinfo(ai); return -1; } } struct evdns_getaddrinfo_request * evdns_getaddrinfo(struct evdns_base *dns_base, const char *nodename, const char *servname, const struct evutil_addrinfo *hints_in, evdns_getaddrinfo_cb cb, void *arg) { struct evdns_getaddrinfo_request *data; struct evutil_addrinfo hints; struct evutil_addrinfo *res = NULL; int err; int port = 0; int want_cname = 0; int started = 0; if (!dns_base) { dns_base = current_base; if (!dns_base) { log(EVDNS_LOG_WARN, "Call to getaddrinfo_async with no " "evdns_base configured."); cb(EVUTIL_EAI_FAIL, NULL, arg); /* ??? better error? */ return NULL; } } /* If we _must_ answer this immediately, do so. */ if ((hints_in && (hints_in->ai_flags & EVUTIL_AI_NUMERICHOST))) { res = NULL; err = evutil_getaddrinfo(nodename, servname, hints_in, &res); cb(err, res, arg); return NULL; } if (hints_in) { memcpy(&hints, hints_in, sizeof(hints)); } else { memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; } evutil_adjust_hints_for_addrconfig_(&hints); /* Now try to see if we _can_ answer immediately. */ /* (It would be nice to do this by calling getaddrinfo directly, with * AI_NUMERICHOST, on plaforms that have it, but we can't: there isn't * a reliable way to distinguish the "that wasn't a numeric host!" case * from any other EAI_NONAME cases.) */ err = evutil_getaddrinfo_common_(nodename, servname, &hints, &res, &port); if (err != EVUTIL_EAI_NEED_RESOLVE) { cb(err, res, arg); return NULL; } /* If there is an entry in the hosts file, we should give it now. */ if (!evdns_getaddrinfo_fromhosts(dns_base, nodename, &hints, port, &res)) { cb(0, res, arg); return NULL; } /* Okay, things are serious now. We're going to need to actually * launch a request. */ data = mm_calloc(1,sizeof(struct evdns_getaddrinfo_request)); if (!data) { cb(EVUTIL_EAI_MEMORY, NULL, arg); return NULL; } memcpy(&data->hints, &hints, sizeof(data->hints)); data->port = (ev_uint16_t)port; data->ipv4_request.type = DNS_IPv4_A; data->ipv6_request.type = DNS_IPv6_AAAA; data->user_cb = cb; data->user_data = arg; data->evdns_base = dns_base; want_cname = (hints.ai_flags & EVUTIL_AI_CANONNAME); /* If we are asked for a PF_UNSPEC address, we launch two requests in * parallel: one for an A address and one for an AAAA address. We * can't send just one request, since many servers only answer one * question per DNS request. * * Once we have the answer to one request, we allow for a short * timeout before we report it, to see if the other one arrives. If * they both show up in time, then we report both the answers. * * If too many addresses of one type time out or fail, we should stop * launching those requests. (XXX we don't do that yet.) */ EVDNS_LOCK(dns_base); if (hints.ai_family != PF_INET6) { log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv4 as %p", nodename, &data->ipv4_request); data->ipv4_request.r = evdns_base_resolve_ipv4(dns_base, nodename, 0, evdns_getaddrinfo_gotresolve, &data->ipv4_request); if (want_cname && data->ipv4_request.r) data->ipv4_request.r->current_req->put_cname_in_ptr = &data->cname_result; } if (hints.ai_family != PF_INET) { log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv6 as %p", nodename, &data->ipv6_request); data->ipv6_request.r = evdns_base_resolve_ipv6(dns_base, nodename, 0, evdns_getaddrinfo_gotresolve, &data->ipv6_request); if (want_cname && data->ipv6_request.r) data->ipv6_request.r->current_req->put_cname_in_ptr = &data->cname_result; } evtimer_assign(&data->timeout, dns_base->event_base, evdns_getaddrinfo_timeout_cb, data); started = (data->ipv4_request.r || data->ipv6_request.r); EVDNS_UNLOCK(dns_base); if (started) { return data; } else { mm_free(data); cb(EVUTIL_EAI_FAIL, NULL, arg); return NULL; } } void evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *data) { EVDNS_LOCK(data->evdns_base); if (data->request_done) { EVDNS_UNLOCK(data->evdns_base); return; } event_del(&data->timeout); data->user_canceled = 1; if (data->ipv4_request.r) evdns_cancel_request(data->evdns_base, data->ipv4_request.r); if (data->ipv6_request.r) evdns_cancel_request(data->evdns_base, data->ipv6_request.r); EVDNS_UNLOCK(data->evdns_base); } libevent-2.1.13-stable/signal.c0000644000175000017500000003036715215775075015352 0ustar00nickmnickm/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ /* * Copyright 2000-2007 Niels Provos * Copyright 2007-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "event2/event-config.h" #include "evconfig-private.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #undef WIN32_LEAN_AND_MEAN #endif #include #ifdef EVENT__HAVE_SYS_TIME_H #include #endif #include #ifdef EVENT__HAVE_SYS_SOCKET_H #include #endif #include #include #include #include #ifdef EVENT__HAVE_UNISTD_H #include #endif #include #ifdef EVENT__HAVE_FCNTL_H #include #endif #include "event2/event.h" #include "event2/event_struct.h" #include "event-internal.h" #include "event2/util.h" #include "evsignal-internal.h" #include "log-internal.h" #include "evmap-internal.h" #include "evthread-internal.h" /* signal.c This is the signal-handling implementation we use for backends that don't have a better way to do signal handling. It uses sigaction() or signal() to set a signal handler, and a socket pair to tell the event base when Note that I said "the event base" : only one event base can be set up to use this at a time. For historical reasons and backward compatibility, if you add an event for a signal to event_base A, then add an event for a signal (any signal!) to event_base B, event_base B will get informed about the signal, but event_base A won't. It would be neat to change this behavior in some future version of Libevent. kqueue already does something far more sensible. We can make all backends on Linux do a reasonable thing using signalfd. */ #ifndef _WIN32 /* Windows wants us to call our signal handlers as __cdecl. Nobody else * expects you to do anything crazy like this. */ #ifndef __cdecl #define __cdecl #endif #endif static int evsig_add(struct event_base *, evutil_socket_t, short, short, void *); static int evsig_del(struct event_base *, evutil_socket_t, short, short, void *); static const struct eventop evsigops = { "signal", NULL, evsig_add, evsig_del, NULL, NULL, 0, 0, 0 }; #ifndef EVENT__DISABLE_THREAD_SUPPORT /* Lock for evsig_base and evsig_base_n_signals_added fields. */ static void *evsig_base_lock = NULL; #endif /* The event base that's currently getting informed about signals. */ static struct event_base *evsig_base = NULL; /* A copy of evsig_base->sigev_n_signals_added. */ static int evsig_base_n_signals_added = 0; static evutil_socket_t evsig_base_fd = -1; static void __cdecl evsig_handler(int sig); #define EVSIGBASE_LOCK() EVLOCK_LOCK(evsig_base_lock, 0) #define EVSIGBASE_UNLOCK() EVLOCK_UNLOCK(evsig_base_lock, 0) void evsig_set_base_(struct event_base *base) { EVSIGBASE_LOCK(); evsig_base = base; evsig_base_n_signals_added = base->sig.ev_n_signals_added; evsig_base_fd = base->sig.ev_signal_pair[1]; EVSIGBASE_UNLOCK(); } /* Callback for when the signal handler write a byte to our signaling socket */ static void evsig_cb(evutil_socket_t fd, short what, void *arg) { static char signals[1024]; ev_ssize_t n; int i; int ncaught[NSIG]; struct event_base *base; base = arg; memset(&ncaught, 0, sizeof(ncaught)); while (1) { #ifdef _WIN32 n = recv(fd, signals, sizeof(signals), 0); #else n = read(fd, signals, sizeof(signals)); #endif if (n == -1) { int err = evutil_socket_geterror(fd); if (! EVUTIL_ERR_RW_RETRIABLE(err)) event_sock_err(1, fd, "%s: recv", __func__); break; } else if (n == 0) { /* XXX warn? */ break; } for (i = 0; i < n; ++i) { ev_uint8_t sig = signals[i]; if (sig < NSIG) ncaught[sig]++; } } EVBASE_ACQUIRE_LOCK(base, th_base_lock); for (i = 0; i < NSIG; ++i) { if (ncaught[i]) evmap_signal_active_(base, i, ncaught[i]); } EVBASE_RELEASE_LOCK(base, th_base_lock); } int evsig_init_(struct event_base *base) { /* * Our signal handler is going to write to one end of the socket * pair to wake up our event loop. The event loop then scans for * signals that got delivered. */ if (evutil_make_internal_pipe_(base->sig.ev_signal_pair) == -1) { #ifdef _WIN32 /* Make this nonfatal on win32, where sometimes people have localhost firewalled. */ event_sock_warn(-1, "%s: socketpair", __func__); #else event_sock_err(1, -1, "%s: socketpair", __func__); #endif return -1; } if (base->sig.sh_old) { mm_free(base->sig.sh_old); } base->sig.sh_old = NULL; base->sig.sh_old_max = 0; event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[0], EV_READ | EV_PERSIST, evsig_cb, base); base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL; event_priority_set(&base->sig.ev_signal, 0); base->evsigsel = &evsigops; return 0; } /* Helper: set the signal handler for evsignal to handler in base, so that * we can restore the original handler when we clear the current one. */ int evsig_set_handler_(struct event_base *base, int evsignal, void (__cdecl *handler)(int)) { #ifdef EVENT__HAVE_SIGACTION struct sigaction sa; #else ev_sighandler_t sh; #endif struct evsig_info *sig = &base->sig; void *p; /* * resize saved signal handler array up to the highest signal number. * a dynamic array is used to keep footprint on the low side. */ if (evsignal >= sig->sh_old_max) { int new_max = evsignal + 1; event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing", __func__, evsignal, sig->sh_old_max)); p = mm_realloc(sig->sh_old, new_max * sizeof(*sig->sh_old)); if (p == NULL) { event_warn("realloc"); return (-1); } memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old), 0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old)); sig->sh_old_max = new_max; sig->sh_old = p; } /* allocate space for previous handler out of dynamic array */ sig->sh_old[evsignal] = mm_malloc(sizeof *sig->sh_old[evsignal]); if (sig->sh_old[evsignal] == NULL) { event_warn("malloc"); return (-1); } /* save previous handler and setup new handler */ #ifdef EVENT__HAVE_SIGACTION memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sa.sa_flags |= SA_RESTART; sigfillset(&sa.sa_mask); if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) { event_warn("sigaction"); mm_free(sig->sh_old[evsignal]); sig->sh_old[evsignal] = NULL; return (-1); } #else if ((sh = signal(evsignal, handler)) == SIG_ERR) { event_warn("signal"); mm_free(sig->sh_old[evsignal]); sig->sh_old[evsignal] = NULL; return (-1); } *sig->sh_old[evsignal] = sh; #endif return (0); } static int evsig_add(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p) { struct evsig_info *sig = &base->sig; (void)p; EVUTIL_ASSERT(evsignal >= 0 && evsignal < NSIG); /* catch signals if they happen quickly */ EVSIGBASE_LOCK(); if (evsig_base != base && evsig_base_n_signals_added) { event_warnx("Added a signal to event base %p with signals " "already added to event_base %p. Only one can have " "signals at a time with the %s backend. The base with " "the most recently added signal or the most recent " "event_base_loop() call gets preference; do " "not rely on this behavior in future Libevent versions.", base, evsig_base, base->evsel->name); } evsig_base = base; evsig_base_n_signals_added = ++sig->ev_n_signals_added; evsig_base_fd = base->sig.ev_signal_pair[1]; EVSIGBASE_UNLOCK(); event_debug(("%s: %d: changing signal handler", __func__, (int)evsignal)); if (evsig_set_handler_(base, (int)evsignal, evsig_handler) == -1) { goto err; } if (!sig->ev_signal_added) { if (event_add_nolock_(&sig->ev_signal, NULL, 0)) goto err; sig->ev_signal_added = 1; } return (0); err: EVSIGBASE_LOCK(); --evsig_base_n_signals_added; --sig->ev_n_signals_added; EVSIGBASE_UNLOCK(); return (-1); } int evsig_restore_handler_(struct event_base *base, int evsignal) { int ret = 0; struct evsig_info *sig = &base->sig; #ifdef EVENT__HAVE_SIGACTION struct sigaction *sh; #else ev_sighandler_t *sh; #endif if (evsignal >= sig->sh_old_max) { /* Can't actually restore. */ /* XXXX.*/ return 0; } /* restore previous handler */ sh = sig->sh_old[evsignal]; sig->sh_old[evsignal] = NULL; #ifdef EVENT__HAVE_SIGACTION if (sigaction(evsignal, sh, NULL) == -1) { event_warn("sigaction"); ret = -1; } #else if (signal(evsignal, *sh) == SIG_ERR) { event_warn("signal"); ret = -1; } #endif mm_free(sh); return ret; } static int evsig_del(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p) { EVUTIL_ASSERT(evsignal >= 0 && evsignal < NSIG); event_debug(("%s: "EV_SOCK_FMT": restoring signal handler", __func__, EV_SOCK_ARG(evsignal))); EVSIGBASE_LOCK(); --evsig_base_n_signals_added; --base->sig.ev_n_signals_added; EVSIGBASE_UNLOCK(); return (evsig_restore_handler_(base, (int)evsignal)); } static void __cdecl evsig_handler(int sig) { int save_errno = errno; #ifdef _WIN32 int socket_errno = EVUTIL_SOCKET_ERROR(); #endif ev_uint8_t msg; if (evsig_base == NULL) { event_warnx( "%s: received signal %d, but have no base configured", __func__, sig); return; } #ifndef EVENT__HAVE_SIGACTION signal(sig, evsig_handler); #endif /* Wake up our notification mechanism */ msg = sig; #ifdef _WIN32 send(evsig_base_fd, (char*)&msg, 1, 0); #else { int r = write(evsig_base_fd, (char*)&msg, 1); (void)r; /* Suppress 'unused return value' and 'unused var' */ } #endif errno = save_errno; #ifdef _WIN32 EVUTIL_SET_SOCKET_ERROR(socket_errno); #endif } void evsig_dealloc_(struct event_base *base) { int i = 0; if (base->sig.ev_signal_added) { event_del(&base->sig.ev_signal); base->sig.ev_signal_added = 0; } /* debug event is created in evsig_init_/event_assign even when * ev_signal_added == 0, so unassign is required */ event_debug_unassign(&base->sig.ev_signal); for (i = 0; i < NSIG; ++i) { if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL) evsig_restore_handler_(base, i); } EVSIGBASE_LOCK(); if (base == evsig_base) { evsig_base = NULL; evsig_base_n_signals_added = 0; evsig_base_fd = -1; } EVSIGBASE_UNLOCK(); if (base->sig.ev_signal_pair[0] != -1) { evutil_closesocket(base->sig.ev_signal_pair[0]); base->sig.ev_signal_pair[0] = -1; } if (base->sig.ev_signal_pair[1] != -1) { evutil_closesocket(base->sig.ev_signal_pair[1]); base->sig.ev_signal_pair[1] = -1; } base->sig.sh_old_max = 0; /* per index frees are handled in evsig_del() */ if (base->sig.sh_old) { mm_free(base->sig.sh_old); base->sig.sh_old = NULL; } } static void evsig_free_globals_locks(void) { #ifndef EVENT__DISABLE_THREAD_SUPPORT if (evsig_base_lock != NULL) { EVTHREAD_FREE_LOCK(evsig_base_lock, 0); evsig_base_lock = NULL; } #endif return; } void evsig_free_globals_(void) { evsig_free_globals_locks(); } #ifndef EVENT__DISABLE_THREAD_SUPPORT int evsig_global_setup_locks_(const int enable_locks) { EVTHREAD_SETUP_GLOBAL_LOCK(evsig_base_lock, 0); return 0; } #endif libevent-2.1.13-stable/build-aux/0000755000175000017500000000000015221201660015567 5ustar00nickmnickmlibevent-2.1.13-stable/build-aux/test-driver0000755000175000017500000001214415215775155020011 0ustar00nickmnickm#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2025-06-18.21; # UTC # Copyright (C) 2011-2025 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <. GNU Automake home page: . General help using GNU software: . END } test_name= # Used for reporting. log_file= # Where to save the output of the test script. trs_file= # Where to save the metadata of the test run. expect_failure=no color_tests=no collect_skipped_logs=yes enable_hard_errors=yes while test $# -gt 0; do case $1 in --help) print_usage; exit $?;; --version) echo "test-driver (GNU Automake) $scriptversion"; exit $?;; --test-name) test_name=$2; shift;; --log-file) log_file=$2; shift;; --trs-file) trs_file=$2; shift;; --color-tests) color_tests=$2; shift;; --collect-skipped-logs) collect_skipped_logs=$2; shift;; --expect-failure) expect_failure=$2; shift;; --enable-hard-errors) enable_hard_errors=$2; shift;; --) shift; break;; -*) usage_error "invalid option: '$1'";; *) break;; esac shift done missing_opts= test x"$test_name" = x && missing_opts="$missing_opts --test-name" test x"$log_file" = x && missing_opts="$missing_opts --log-file" test x"$trs_file" = x && missing_opts="$missing_opts --trs-file" if test x"$missing_opts" != x; then usage_error "the following mandatory options are missing:$missing_opts" fi if test $# -eq 0; then usage_error "missing argument" fi if test $color_tests = yes; then # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'. red='' # Red. grn='' # Green. lgn='' # Light green. blu='' # Blue. mgn='' # Magenta. std='' # No color. else red= grn= lgn= blu= mgn= std= fi do_exit='rm -f $log_file $trs_file; (exit $st); exit $st' trap "st=129; $do_exit" 1 trap "st=130; $do_exit" 2 trap "st=141; $do_exit" 13 trap "st=143; $do_exit" 15 # Test script is run here. We create the file first, then append to it, # to ameliorate tests themselves also writing to the log file. Our tests # don't, but others can (automake bug#35762). : >"$log_file" "$@" >>"$log_file" 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=$collect_skipped_logs;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>"$log_file" # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "scriptversion=" # time-stamp-format: "%Y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libevent-2.1.13-stable/build-aux/missing0000755000175000017500000001706515215775155017221 0ustar00nickmnickm#! /bin/sh # Common wrapper for a few potentially missing GNU and other programs. scriptversion=2025-06-18.21; # UTC # shellcheck disable=SC2006,SC2268 # we must support pre-POSIX shells # Copyright (C) 1996-2025 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autogen autoheader autom4te automake autoreconf bison flex help2man lex makeinfo perl yacc Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Report bugs to . GNU Automake home page: . General help using GNU software: ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing (GNU Automake) $scriptversion" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake|autoreconf) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; *) : ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" autoheader_deps="'acconfig.h'" automake_deps="'Makefile.am'" aclocal_deps="'acinclude.m4'" case $normalized_program in aclocal*) echo "You should only need it if you modified $aclocal_deps or" echo "$configure_deps." ;; autoconf*) echo "You should only need it if you modified $configure_deps." ;; autogen*) echo "You should only need it if you modified a '.def' or '.tpl' file." echo "You may want to install the GNU AutoGen package:" echo "<$gnu_software_URL/autogen/>" ;; autoheader*) echo "You should only need it if you modified $autoheader_deps or" echo "$configure_deps." ;; automake*) echo "You should only need it if you modified $automake_deps or" echo "$configure_deps." ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." ;; autoreconf*) echo "You should only need it if you modified $aclocal_deps or" echo "$automake_deps or $autoheader_deps or $automake_deps or" echo "$configure_deps." ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; perl*) echo "You should only need it to run GNU Autoconf, GNU Automake, " echo " assorted other tools, or if you modified a Perl source file." echo "You may want to install the Perl 5 language interpreter:" echo "<$perl_URL>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac program_details "$normalized_program" } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "scriptversion=" # time-stamp-format: "%Y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: libevent-2.1.13-stable/build-aux/ltmain.sh0000644000175000017500000122264715215775153017445 0ustar00nickmnickm#! /usr/bin/env sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2019-02-19.15 # libtool (GNU libtool) 2.5.4 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2019, 2021-2024 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION=2.5.4 package_revision=2.5.4 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2019-02-19.15; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2004-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # These NLS vars are set unconditionally (bootstrap issue #24). Unset those # in case the environment reset is needed later and the $save_* variant is not # defined (see the code above). LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # func_unset VAR # -------------- # Portably unset VAR. # In some shells, an 'unset VAR' statement leaves a non-zero return # status if VAR is already unset, which might be problematic if the # statement is used at the end of a function (thus poisoning its return # value) or when 'set -e' is active (causing even a spurious abort of # the script in this case). func_unset () { { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } } # Make sure CDPATH doesn't cause `cd` commands to output the target dir. func_unset CDPATH # Make sure ${,E,F}GREP behave sanely. func_unset GREP_OPTIONS ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" # require_check_ifs_backslash # --------------------------- # Check if we can use backslash as IFS='\' separator, and set # $check_ifs_backshlash_broken to ':' or 'false'. require_check_ifs_backslash=func_require_check_ifs_backslash func_require_check_ifs_backslash () { _G_save_IFS=$IFS IFS='\' _G_check_ifs_backshlash='a\\b' for _G_i in $_G_check_ifs_backshlash do case $_G_i in a) check_ifs_backshlash_broken=false ;; '') break ;; *) check_ifs_backshlash_broken=: break ;; esac done IFS=$_G_save_IFS require_check_ifs_backslash=: } ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # usable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1=\$$1\\ \$func_quote_arg_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value returned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list in case some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_portable EVAL ARG # ---------------------------- # Internal function to portably implement func_quote_arg. Note that we still # keep attention to performance here so we as much as possible try to avoid # calling sed binary (so far O(N) complexity as long as func_append is O(1)). func_quote_portable () { $debug_cmd $require_check_ifs_backslash func_quote_portable_result=$2 # one-time-loop (easy break) while true do if $1; then func_quote_portable_result=`$ECHO "$2" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` break fi # Quote for eval. case $func_quote_portable_result in *[\\\`\"\$]*) # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string # contains the shell wildcard characters. case $check_ifs_backshlash_broken$func_quote_portable_result in :*|*[\[\*\?]*) func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ | $SED "$sed_quote_subst"` break ;; esac func_quote_portable_old_IFS=$IFS for _G_char in '\' '`' '"' '$' do # STATE($1) PREV($2) SEPARATOR($3) set start "" "" func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy IFS=$_G_char for _G_part in $func_quote_portable_result do case $1 in quote) func_append func_quote_portable_result "$3$2" set quote "$_G_part" "\\$_G_char" ;; start) set first "" "" func_quote_portable_result= ;; first) set quote "$_G_part" "" ;; esac done done IFS=$func_quote_portable_old_IFS ;; *) ;; esac break done func_quote_portable_unquoted_result=$func_quote_portable_result case $func_quote_portable_result in # double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # many bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_portable_result=\"$func_quote_portable_result\" ;; esac } # func_quotefast_eval ARG # ----------------------- # Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', # but optimized for speed. Result is stored in $func_quotefast_eval. if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then printf -v _GL_test_printf_tilde %q '~' if test '\~' = "$_GL_test_printf_tilde"; then func_quotefast_eval () { printf -v func_quotefast_eval_result %q "$1" } else # Broken older Bash implementations. Make those faster too if possible. func_quotefast_eval () { case $1 in '~'*) func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result ;; *) printf -v func_quotefast_eval_result %q "$1" ;; esac } fi else func_quotefast_eval () { func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result } fi # func_quote_arg MODEs ARG # ------------------------ # Quote one ARG to be evaled later. MODEs argument may contain zero or more # specifiers listed below separated by ',' character. This function returns two # values: # i) func_quote_arg_result # double-quoted (when needed), suitable for a subsequent eval # ii) func_quote_arg_unquoted_result # has all characters that are still active within double # quotes backslashified. Available only if 'unquoted' is specified. # # Available modes: # ---------------- # 'eval' (default) # - escape shell special characters # 'expand' # - the same as 'eval'; but do not quote variable references # 'pretty' # - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might # be used later in func_quote to get output like: 'echo "a b"' instead # of 'echo a\ b'. This is slower than default on some shells. # 'unquoted' # - produce also $func_quote_arg_unquoted_result which does not contain # wrapping double-quotes. # # Examples for 'func_quote_arg pretty,unquoted string': # # string | *_result | *_unquoted_result # ------------+-----------------------+------------------- # " | \" | \" # a b | "a b" | a b # "a b" | "\"a b\"" | \"a b\" # * | "*" | * # z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" # # Examples for 'func_quote_arg pretty,unquoted,expand string': # # string | *_result | *_unquoted_result # --------------+---------------------+-------------------- # z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" func_quote_arg () { _G_quote_expand=false case ,$1, in *,expand,*) _G_quote_expand=: ;; esac case ,$1, in *,pretty,*|*,expand,*|*,unquoted,*) func_quote_portable $_G_quote_expand "$2" func_quote_arg_result=$func_quote_portable_result func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result ;; *) # Faster quote-for-eval for some shells. func_quotefast_eval "$2" func_quote_arg_result=$func_quotefast_eval_result ;; esac } # func_quote MODEs ARGs... # ------------------------ # Quote all ARGs to be evaled later and join them into single command. See # func_quote_arg's description for more info. func_quote () { $debug_cmd _G_func_quote_mode=$1 ; shift func_quote_result= while test 0 -lt $#; do func_quote_arg "$_G_func_quote_mode" "$1" if test -n "$func_quote_result"; then func_append func_quote_result " $func_quote_arg_result" else func_append func_quote_result "$func_quote_arg_result" fi shift done } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_arg pretty,expand "$_G_cmd" eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_arg expand,pretty "$_G_cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Set a version string for this script. scriptversion=2019-02-19.15; # UTC ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # in the main code. A hook is just a list of function names that can be # run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of hook functions to be called by # FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_propagate_result FUNC_NAME_A FUNC_NAME_B # --------------------------------------------- # If the *_result variable of FUNC_NAME_A _is set_, assign its value to # *_result variable of FUNC_NAME_B. func_propagate_result () { $debug_cmd func_propagate_result_result=: if eval "test \"\${${1}_result+set}\" = set" then eval "${2}_result=\$${1}_result" else func_propagate_result_result=false fi } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do func_unset "${_G_hook}_result" eval $_G_hook '${1+"$@"}' func_propagate_result $_G_hook func_run_hooks if $func_propagate_result_result; then eval set dummy "$func_run_hooks_result"; shift fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list from your hook function. You may remove # or edit any options that you action, and then pass back the remaining # unprocessed options in '_result', escaped # suitably for 'eval'. # # The '_result' variable is automatically unset # before your hook gets called; for best performance, only set the # *_result variable when necessary (i.e. don't call the 'func_quote' # function unnecessarily because it can be an expensive operation on some # machines). # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). Leave # # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@" in case we need it later, # # if $args_changed was set to 'true'. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # # Only call 'func_quote' here if we processed at least one argument. # if $args_changed; then # func_quote eval ${1+"$@"} # my_silent_option_result=$func_quote_result # fi # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd func_run_hooks func_options ${1+"$@"} func_propagate_result func_run_hooks func_options_finish } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_options_quoted=false for my_func in options_prep parse_options validate_options options_finish do func_unset func_${my_func}_result func_unset func_run_hooks_result eval func_$my_func '${1+"$@"}' func_propagate_result func_$my_func func_options if $func_propagate_result_result; then eval set dummy "$func_options_result"; shift _G_options_quoted=: fi done $_G_options_quoted || { # As we (func_options) are top-level options-parser function and # nobody quoted "$@" for us yet, we need to do it explicitly for # caller. func_quote eval ${1+"$@"} func_options_result=$func_quote_result } } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} func_propagate_result func_run_hooks func_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} func_propagate_result func_run_hooks func_parse_options if $func_propagate_result_result; then eval set dummy "$func_parse_options_result"; shift # Even though we may have changed "$@", we passed the "$@" array # down into the hook and it quoted it for us (because we are in # this if-branch). No need to quote it again. _G_parse_options_requote=false fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break # We expect that one of the options parsed in this function matches # and thus we remove _G_opt from "$@" and need to re-quote. _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" >&2 $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_parse_options_requote=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac if $_G_match_parse_options; then _G_parse_options_requote=: fi done if $_G_parse_options_requote; then # save modified positional parameters for caller func_quote eval ${1+"$@"} func_parse_options_result=$func_quote_result fi } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables # after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} if test "x$func_split_equals_lhs" = "x$1"; then func_split_equals_rhs= fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs=" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. # The version message is extracted from the calling file's header # comments, with leading '# ' stripped: # 1. First display the progname and version # 2. Followed by the header comment line matching /^# Written by / # 3. Then a blank line followed by the first following line matching # /^# Copyright / # 4. Immediately followed by any lines between the previous matches, # except lines preceding the intervening completely blank line. # For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /^# Written by /!b s|^# ||; p; n :fwd2blnk /./ { n b fwd2blnk } p; n :holdwrnt s|^# || s|^# *$|| /^Copyright /!{ /./H n b holdwrnt } s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| G s|\(\n\)\n*|\1|g p; q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.5.4' # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd year=`date +%Y` cat < This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Originally written by Gordon Matzigkeit, 1996 (See AUTHORS for complete contributor listing) EOF exit $? } # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information --finish use operation '--mode=finish' --mode=MODE use operation mode MODE --no-finish don't update shared library cache --no-quiet, --no-silent print default informational messages --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --reorder-cache=DIRS reorder shared library cache for preferred DIRS --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_reorder_cache=false opt_preserve_dup_deps=false opt_quiet=false opt_finishing=true opt_warning= nonopt= preserve_args= _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote eval ${1+"$@"} libtool_options_prep_result=$func_quote_result fi } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument '$1' for $_G_opt" exit_cmd=exit ;; esac shift ;; --no-finish) opt_finishing=false func_append preserve_args " $_G_opt" ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --reorder-cache) opt_reorder_cache=true shared_lib_dirs=$1 if test -n "$shared_lib_dirs"; then case $1 in # Must begin with /: /*) ;; # Catch anything else as an error (relative paths) *) func_error "invalid argument '$1' for $_G_opt" func_error "absolute paths are required for $_G_opt" exit_cmd=exit ;; esac fi shift ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote eval ${1+"$@"} libtool_parse_options_result=$func_quote_result fi } func_add_hook func_parse_options libtool_parse_options # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { if $opt_warning; then $debug_cmd $warning_func ${1+"$@"} fi } # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" # Keeping compiler generated duplicates in $postdeps and $predeps is not # harmful, and is necessary in a majority of systems that use it to satisfy # symbol dependencies. opt_duplicate_compiler_generated_deps=: $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote eval ${1+"$@"} libtool_validate_options_result=$func_quote_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, windows, cygwin, or some other w32 environment. Relies on a # correctly configured wine environment available, with the winepath program # in $build's $PATH. Assumes ARG has no leading or trailing path separator # characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep # func_convert_delimited_path PATH ORIG_DELIMITER NEW_DELIMITER # Replaces a delimiter for a given path. func_convert_delimited_path () { converted_path=`$ECHO "$1" | $SED "s#$2#$3#g"` } # end func_convert_delimited_path ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_reorder_shared_lib_cache DIRS # Reorder the shared library cache by unconfiguring previous shared library cache # and configuring preferred search directories before previous search directories. # Previous shared library cache: /usr/lib /usr/local/lib # Preferred search directories: /tmp/testing # Reordered shared library cache: /tmp/testing /usr/lib /usr/local/lib func_reorder_shared_lib_cache () { $debug_cmd case $host_os in openbsd*) get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"` func_convert_delimited_path "$get_search_directories" ':' '\ ' save_search_directories=$converted_path func_convert_delimited_path "$1" ':' '\ ' # Ensure directories exist for dir in $converted_path; do # Ensure each directory is an absolute path case $dir in /*) ;; *) func_error "Directory '$dir' is not an absolute path" exit $EXIT_FAILURE ;; esac # Ensure no trailing slashes func_stripname '' '/' "$dir" dir=$func_stripname_result if test -d "$dir"; then if test -n "$preferred_search_directories"; then preferred_search_directories="$preferred_search_directories $dir" else preferred_search_directories=$dir fi else func_error "Directory '$dir' does not exist" exit $EXIT_FAILURE fi done PATH="$PATH:/sbin" ldconfig -U $save_search_directories PATH="$PATH:/sbin" ldconfig -m $preferred_search_directories $save_search_directories get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"` func_convert_delimited_path "$get_search_directories" ':' '\ ' reordered_search_directories=$converted_path $ECHO "Original: $save_search_directories" $ECHO "Reordered: $reordered_search_directories" exit $EXIT_SUCCESS ;; *) func_error "--reorder-cache is not supported for host_os=$host_os." exit $EXIT_FAILURE ;; esac } # end func_reorder_shared_lib_cache # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_arg pretty "$libobj" test "X$libobj" != "X$func_quote_arg_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | windows* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_arg pretty "$srcfile" qsrcfile=$func_quote_arg_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG -Xcompiler FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wa,FLAG -Xassembler FLAG pass linker-specific FLAG directly to the assembler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # If option '--reorder-cache', reorder the shared library cache and exit. if $opt_reorder_cache; then func_reorder_shared_lib_cache $shared_lib_dirs fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs" && $opt_finishing; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done if test "false" = "$opt_finishing"; then echo echo "NOTE: finish_cmds were not executed during testing, so you must" echo "manually run ldconfig to add a given test directory, LIBDIR, to" echo "the search path for generated executables." fi echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_arg pretty "$nonopt" install_prog="$func_quote_arg_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_arg pretty "$arg" func_append install_prog "$func_quote_arg_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_arg pretty "$arg" func_append install_prog " $func_quote_arg_result" if test -n "$arg2"; then func_quote_arg pretty "$arg2" fi func_append install_shared_prog " $func_quote_arg_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_arg pretty "$install_override_mode" func_append install_shared_prog " -m $func_quote_arg_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Strip any trailing slash from the destination. func_stripname '' '/' "$libdir" destlibdir=$func_stripname_result func_stripname '' '/' "$destdir" s_destdir=$func_stripname_result # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "X$s_destdir" | $Xsed -e "s%$destlibdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | windows* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw* | *windows*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_arg expand,pretty "$relink_command" eval "func_echo $func_quote_arg_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 case $host in i[3456]86-*-mingw32*) eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" ;; *) eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/__nm_//' >> '$nlist'" ;; esac } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64|pe-aarch64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw/windows # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw/windows-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" func_quote_arg pretty "$ECHO" qECHO=$func_quote_arg_result $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=$qECHO fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw* | *-*-windows* | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw/windows when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #if defined _WIN32 && !defined __GNUC__ # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ _CRTIMP int __cdecl _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw* | windows*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= compile_rpath_tail= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= temp_rpath_tail= thread_safe=no vinfo= vinfo_number=no weak_libs= rpath_arg= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_arg pretty,unquoted "$arg" qarg=$func_quote_arg_unquoted_result func_append libtool_args " $func_quote_arg_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "argument to -rpath is not absolute: $arg" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xassembler) func_append compiler_flags " -Xassembler $qarg" prev= func_append compile_command " -Xassembler $qarg" func_append finalize_command " -Xassembler $qarg" continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. # -q