/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * 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. */ #include "uv.h" #include "uv-common.h" #include #include #include #include /* NULL */ #include /* malloc */ #include /* memset */ #if defined(_WIN32) # include /* malloc */ #else # include /* if_nametoindex */ #endif typedef struct { uv_malloc_func local_malloc; uv_realloc_func local_realloc; uv_calloc_func local_calloc; uv_free_func local_free; } uv__allocator_t; static uv__allocator_t uv__allocator = { malloc, realloc, calloc, free, }; char* uv__strdup(const char* s) { size_t len = strlen(s) + 1; char* m = uv__malloc(len); if (m == NULL) return NULL; return memcpy(m, s, len); } char* uv__strndup(const char* s, size_t n) { char* m; size_t len = strlen(s); if (n < len) len = n; m = uv__malloc(len + 1); if (m == NULL) return NULL; m[len] = '\0'; return memcpy(m, s, len); } void* uv__malloc(size_t size) { return uv__allocator.local_malloc(size); } void uv__free(void* ptr) { uv__allocator.local_free(ptr); } void* uv__calloc(size_t count, size_t size) { return uv__allocator.local_calloc(count, size); } void* uv__realloc(void* ptr, size_t size) { return uv__allocator.local_realloc(ptr, size); } int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, uv_free_func free_func) { if (malloc_func == NULL || realloc_func == NULL || calloc_func == NULL || free_func == NULL) { return UV_EINVAL; } uv__allocator.local_malloc = malloc_func; uv__allocator.local_realloc = realloc_func; uv__allocator.local_calloc = calloc_func; uv__allocator.local_free = free_func; return 0; } #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t); size_t uv_handle_size(uv_handle_type type) { switch (type) { UV_HANDLE_TYPE_MAP(XX) default: return -1; } } size_t uv_req_size(uv_req_type type) { switch(type) { UV_REQ_TYPE_MAP(XX) default: return -1; } } #undef XX size_t uv_loop_size(void) { return sizeof(uv_loop_t); } uv_buf_t uv_buf_init(char* base, unsigned int len) { uv_buf_t buf; buf.base = base; buf.len = len; return buf; } static const char* uv__unknown_err_code(int err) { char buf[32]; char* copy; snprintf(buf, sizeof(buf), "Unknown system error %d", err); copy = uv__strdup(buf); return copy != NULL ? copy : "Unknown system error"; } #define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name; const char* uv_err_name(int err) { switch (err) { UV_ERRNO_MAP(UV_ERR_NAME_GEN) } return uv__unknown_err_code(err); } #undef UV_ERR_NAME_GEN #define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg; const char* uv_strerror(int err) { switch (err) { UV_ERRNO_MAP(UV_STRERROR_GEN) } return uv__unknown_err_code(err); } #undef UV_STRERROR_GEN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) { memset(addr, 0, sizeof(*addr)); addr->sin_family = AF_INET; addr->sin_port = htons(port); return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr)); } int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) { char address_part[40]; size_t address_part_size; const char* zone_index; memset(addr, 0, sizeof(*addr)); addr->sin6_family = AF_INET6; addr->sin6_port = htons(port); zone_index = strchr(ip, '%'); if (zone_index != NULL) { address_part_size = zone_index - ip; if (address_part_size >= sizeof(address_part)) address_part_size = sizeof(address_part) - 1; memcpy(address_part, ip, address_part_size); address_part[address_part_size] = '\0'; ip = address_part; zone_index++; /* skip '%' */ /* NOTE: unknown interface (id=0) is silently ignored */ #ifdef _WIN32 addr->sin6_scope_id = atoi(zone_index); #else addr->sin6_scope_id = if_nametoindex(zone_index); #endif } return uv_inet_pton(AF_INET6, ip, &addr->sin6_addr); } int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) { return uv_inet_ntop(AF_INET, &src->sin_addr, dst, size); } int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) { return uv_inet_ntop(AF_INET6, &src->sin6_addr, dst, size); } int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags) { unsigned int addrlen; if (handle->type != UV_TCP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__tcp_bind(handle, addr, addrlen, flags); } int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__udp_bind(handle, addr, addrlen, flags); } int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, uv_connect_cb cb) { unsigned int addrlen; if (handle->type != UV_TCP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__tcp_connect(req, handle, addr, addrlen, cb); } int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, uv_udp_send_cb send_cb) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb); } int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); else return UV_EINVAL; return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen); } int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { if (handle->type != UV_UDP || alloc_cb == NULL || recv_cb == NULL) return UV_EINVAL; else return uv__udp_recv_start(handle, alloc_cb, recv_cb); } int uv_udp_recv_stop(uv_udp_t* handle) { if (handle->type != UV_UDP) return UV_EINVAL; else return uv__udp_recv_stop(handle); } void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { QUEUE queue; QUEUE* q; uv_handle_t* h; QUEUE_MOVE(&loop->handle_queue, &queue); while (!QUEUE_EMPTY(&queue)) { q = QUEUE_HEAD(&queue); h = QUEUE_DATA(q, uv_handle_t, handle_queue); QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->handle_queue, q); if (h->flags & UV__HANDLE_INTERNAL) continue; walk_cb(h, arg); } } static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) { const char* type; QUEUE* q; uv_handle_t* h; if (loop == NULL) loop = uv_default_loop(); QUEUE_FOREACH(q, &loop->handle_queue) { h = QUEUE_DATA(q, uv_handle_t, handle_queue); if (only_active && !uv__is_active(h)) continue; switch (h->type) { #define X(uc, lc) case UV_##uc: type = #lc; break; UV_HANDLE_TYPE_MAP(X) #undef X default: type = ""; } fprintf(stream, "[%c%c%c] %-8s %p\n", "R-"[!(h->flags & UV__HANDLE_REF)], "A-"[!(h->flags & UV__HANDLE_ACTIVE)], "I-"[!(h->flags & UV__HANDLE_INTERNAL)], type, (void*)h); } } void uv_print_all_handles(uv_loop_t* loop, FILE* stream) { uv__print_handles(loop, 0, stream); } void uv_print_active_handles(uv_loop_t* loop, FILE* stream) { uv__print_handles(loop, 1, stream); } void uv_ref(uv_handle_t* handle) { uv__handle_ref(handle); } void uv_unref(uv_handle_t* handle) { uv__handle_unref(handle); } int uv_has_ref(const uv_handle_t* handle) { return uv__has_ref(handle); } void uv_stop(uv_loop_t* loop) { loop->stop_flag = 1; } uint64_t uv_now(const uv_loop_t* loop) { return loop->time; } size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) { unsigned int i; size_t bytes; bytes = 0; for (i = 0; i < nbufs; i++) bytes += (size_t) bufs[i].len; return bytes; } int uv_recv_buffer_size(uv_handle_t* handle, int* value) { return uv__socket_sockopt(handle, SO_RCVBUF, value); } int uv_send_buffer_size(uv_handle_t* handle, int *value) { return uv__socket_sockopt(handle, SO_SNDBUF, value); } int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) { size_t required_len; if (!uv__is_active(handle)) { *size = 0; return UV_EINVAL; } required_len = strlen(handle->path); if (required_len >= *size) { *size = required_len + 1; return UV_ENOBUFS; } memcpy(buffer, handle->path, required_len); *size = required_len; buffer[required_len] = '\0'; return 0; } /* The windows implementation does not have the same structure layout as * the unix implementation (nbufs is not directly inside req but is * contained in a nested union/struct) so this function locates it. */ static unsigned int* uv__get_nbufs(uv_fs_t* req) { #ifdef _WIN32 return &req->fs.info.nbufs; #else return &req->nbufs; #endif } void uv__fs_scandir_cleanup(uv_fs_t* req) { uv__dirent_t** dents; unsigned int* nbufs = uv__get_nbufs(req); dents = req->ptr; if (*nbufs > 0 && *nbufs != (unsigned int) req->result) (*nbufs)--; for (; *nbufs < (unsigned int) req->result; (*nbufs)++) uv__free(dents[*nbufs]); } int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { uv__dirent_t** dents; uv__dirent_t* dent; unsigned int* nbufs = uv__get_nbufs(req); dents = req->ptr; /* Free previous entity */ if (*nbufs > 0) uv__free(dents[*nbufs - 1]); /* End was already reached */ if (*nbufs == (unsigned int) req->result) { uv__free(dents); req->ptr = NULL; return UV_EOF; } dent = dents[(*nbufs)++]; ent->name = dent->d_name; #ifdef HAVE_DIRENT_TYPES switch (dent->d_type) { case UV__DT_DIR: ent->type = UV_DIRENT_DIR; break; case UV__DT_FILE: ent->type = UV_DIRENT_FILE; break; case UV__DT_LINK: ent->type = UV_DIRENT_LINK; break; case UV__DT_FIFO: ent->type = UV_DIRENT_FIFO; break; case UV__DT_SOCKET: ent->type = UV_DIRENT_SOCKET; break; case UV__DT_CHAR: ent->type = UV_DIRENT_CHAR; break; case UV__DT_BLOCK: ent->type = UV_DIRENT_BLOCK; break; default: ent->type = UV_DIRENT_UNKNOWN; } #else ent->type = UV_DIRENT_UNKNOWN; #endif return 0; } int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) { va_list ap; int err; va_start(ap, option); /* Any platform-agnostic options should be handled here. */ err = uv__loop_configure(loop, option, ap); va_end(ap); return err; } static uv_loop_t default_loop_struct; static uv_loop_t* default_loop_ptr; uv_loop_t* uv_default_loop(void) { if (default_loop_ptr != NULL) return default_loop_ptr; if (uv_loop_init(&default_loop_struct)) return NULL; default_loop_ptr = &default_loop_struct; return default_loop_ptr; } uv_loop_t* uv_loop_new(void) { uv_loop_t* loop; loop = uv__malloc(sizeof(*loop)); if (loop == NULL) return NULL; if (uv_loop_init(loop)) { uv__free(loop); return NULL; } return loop; } int uv_loop_close(uv_loop_t* loop) { QUEUE* q; uv_handle_t* h; if (!QUEUE_EMPTY(&(loop)->active_reqs)) return UV_EBUSY; QUEUE_FOREACH(q, &loop->handle_queue) { h = QUEUE_DATA(q, uv_handle_t, handle_queue); if (!(h->flags & UV__HANDLE_INTERNAL)) return UV_EBUSY; } uv__loop_close(loop); #ifndef NDEBUG memset(loop, -1, sizeof(*loop)); #endif if (loop == default_loop_ptr) default_loop_ptr = NULL; return 0; } void uv_loop_delete(uv_loop_t* loop) { uv_loop_t* default_loop; int err; default_loop = default_loop_ptr; err = uv_loop_close(loop); (void) err; /* Squelch compiler warnings. */ assert(err == 0); if (loop != default_loop) uv__free(loop); }