update esppilight lib

content of the library is not filtered on 433mhz only
This commit is contained in:
Florian committed 2018-11-28 20:41:37 +01:00
1 parent a275e48ee1
commit 67dd7ac078
1030 files changed
+203335 -116

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,703 @@
/*
Copyright (c) 2013, Kenneth MacKay
Copyright (c) 2014, Emergya (Cloud4all, FP7/2007-2013 grant agreement #289016)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* 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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 "android-ifaddrs.h"
#include "uv-common.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
typedef struct NetlinkList
{
struct NetlinkList *m_next;
struct nlmsghdr *m_data;
unsigned int m_size;
} NetlinkList;
static int netlink_socket(void)
{
struct sockaddr_nl l_addr;
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(l_socket < 0)
{
return -1;
}
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
{
close(l_socket);
return -1;
}
return l_socket;
}
static int netlink_send(int p_socket, int p_request)
{
char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
struct nlmsghdr *l_hdr;
struct rtgenmsg *l_msg;
struct sockaddr_nl l_addr;
memset(l_buffer, 0, sizeof(l_buffer));
l_hdr = (struct nlmsghdr *)l_buffer;
l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
l_hdr->nlmsg_type = p_request;
l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
l_hdr->nlmsg_pid = 0;
l_hdr->nlmsg_seq = p_socket;
l_msg->rtgen_family = AF_UNSPEC;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
}
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
{
struct sockaddr_nl l_addr;
struct msghdr l_msg;
struct iovec l_iov;
l_iov.iov_base = p_buffer;
l_iov.iov_len = p_len;
for(;;)
{
int l_result;
l_msg.msg_name = (void *)&l_addr;
l_msg.msg_namelen = sizeof(l_addr);
l_msg.msg_iov = &l_iov;
l_msg.msg_iovlen = 1;
l_msg.msg_control = NULL;
l_msg.msg_controllen = 0;
l_msg.msg_flags = 0;
l_result = recvmsg(p_socket, &l_msg, 0);
if(l_result < 0)
{
if(errno == EINTR)
{
continue;
}
return -2;
}
/* Buffer was too small */
if(l_msg.msg_flags & MSG_TRUNC)
{
return -1;
}
return l_result;
}
}
static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
{
size_t l_size = 4096;
void *l_buffer = NULL;
for(;;)
{
int l_read;
uv__free(l_buffer);
l_buffer = uv__malloc(l_size);
if (l_buffer == NULL)
{
return NULL;
}
l_read = netlink_recv(p_socket, l_buffer, l_size);
*p_size = l_read;
if(l_read == -2)
{
uv__free(l_buffer);
return NULL;
}
if(l_read >= 0)
{
pid_t l_pid = getpid();
struct nlmsghdr *l_hdr;
for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
*p_done = 1;
break;
}
if(l_hdr->nlmsg_type == NLMSG_ERROR)
{
uv__free(l_buffer);
return NULL;
}
}
return l_buffer;
}
l_size *= 2;
}
}
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
{
NetlinkList *l_item = uv__malloc(sizeof(NetlinkList));
if (l_item == NULL)
{
return NULL;
}
l_item->m_next = NULL;
l_item->m_data = p_data;
l_item->m_size = p_size;
return l_item;
}
static void freeResultList(NetlinkList *p_list)
{
NetlinkList *l_cur;
while(p_list)
{
l_cur = p_list;
p_list = p_list->m_next;
uv__free(l_cur->m_data);
uv__free(l_cur);
}
}
static NetlinkList *getResultList(int p_socket, int p_request)
{
int l_size;
int l_done;
NetlinkList *l_list;
NetlinkList *l_end;
if(netlink_send(p_socket, p_request) < 0)
{
return NULL;
}
l_list = NULL;
l_end = NULL;
l_done = 0;
while(!l_done)
{
NetlinkList *l_item;
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
/* Error */
if(!l_hdr)
{
freeResultList(l_list);
return NULL;
}
l_item = newListItem(l_hdr, l_size);
if (!l_item)
{
freeResultList(l_list);
return NULL;
}
if(!l_list)
{
l_list = l_item;
}
else
{
l_end->m_next = l_item;
}
l_end = l_item;
}
return l_list;
}
static size_t maxSize(size_t a, size_t b)
{
return (a > b ? a : b);
}
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
{
switch(p_family)
{
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
case AF_PACKET:
return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
default:
return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
}
}
static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
{
switch(p_family)
{
case AF_INET:
memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
break;
case AF_INET6:
memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
break;
case AF_PACKET:
memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
break;
default:
memcpy(p_dest->sa_data, p_data, p_size);
break;
}
p_dest->sa_family = p_family;
}
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
{
if(!*p_resultList)
{
*p_resultList = p_entry;
}
else
{
struct ifaddrs *l_cur = *p_resultList;
while(l_cur->ifa_next)
{
l_cur = l_cur->ifa_next;
}
l_cur->ifa_next = p_entry;
}
}
static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
{
struct ifaddrs *l_entry;
char *l_index;
char *l_name;
char *l_addr;
char *l_data;
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
size_t l_dataSize = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
struct rtattr *l_rta;
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
break;
case IFLA_IFNAME:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
case IFLA_STATS:
l_dataSize += NLMSG_ALIGN(l_rtaSize);
break;
default:
break;
}
}
l_entry = uv__malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = "";
l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
l_name = l_index + sizeof(int);
l_addr = l_name + l_nameSize;
l_data = l_addr + l_addrSize;
/* Save the interface index so we can look it up when handling the
* addresses.
*/
memcpy(l_index, &l_info->ifi_index, sizeof(int));
l_entry->ifa_flags = l_info->ifi_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
{
size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
if(l_rta->rta_type == IFLA_ADDRESS)
{
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFLA_IFNAME:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
case IFLA_STATS:
memcpy(l_data, l_rtaData, l_rtaDataSize);
l_entry->ifa_data = l_data;
break;
default:
break;
}
}
addToEnd(p_resultList, l_entry);
return 0;
}
static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
{
int l_num = 0;
struct ifaddrs *l_cur = *p_links;
while(l_cur && l_num < p_numLinks)
{
char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
int l_index;
memcpy(&l_index, l_indexPtr, sizeof(int));
if(l_index == p_index)
{
return l_cur;
}
l_cur = l_cur->ifa_next;
++l_num;
}
return NULL;
}
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
{
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
int l_addedNetmask = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
struct rtattr *l_rta;
struct ifaddrs *l_entry;
char *l_name;
char *l_addr;
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
if(l_info->ifa_family == AF_PACKET)
{
continue;
}
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_LOCAL:
if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
{
/* Make room for netmask */
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
l_addedNetmask = 1;
}
case IFA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
break;
case IFA_LABEL:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
default:
break;
}
}
l_entry = uv__malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
l_addr = l_name + l_nameSize;
l_entry->ifa_flags = l_info->ifa_flags;
if(l_interface)
{
l_entry->ifa_flags |= l_interface->ifa_flags;
}
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_BROADCAST:
case IFA_LOCAL:
{
size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
if(l_info->ifa_family == AF_INET6)
{
if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
{
((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
}
}
/* Apparently in a point-to-point network IFA_ADDRESS contains
* the dest address and IFA_LOCAL contains the local address
*/
if(l_rta->rta_type == IFA_ADDRESS)
{
if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
}
else if(l_rta->rta_type == IFA_LOCAL)
{
if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = l_entry->ifa_addr;
}
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFA_LABEL:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
default:
break;
}
}
if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
{
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
char l_mask[16] = {0};
unsigned i;
for(i=0; i<(l_prefix/8); ++i)
{
l_mask[i] = 0xff;
}
if(l_prefix % 8)
{
l_mask[i] = 0xff << (8 - (l_prefix % 8));
}
makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
l_entry->ifa_netmask = (struct sockaddr *)l_addr;
}
addToEnd(p_resultList, l_entry);
return 0;
}
static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList)
{
int l_numLinks = 0;
pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWLINK)
{
if(interpretLink(l_hdr, p_resultList) == -1)
{
return -1;
}
++l_numLinks;
}
}
}
return l_numLinks;
}
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks)
{
pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWADDR)
{
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
{
return -1;
}
}
}
}
return 0;
}
int getifaddrs(struct ifaddrs **ifap)
{
int l_socket;
int l_result;
int l_numLinks;
NetlinkList *l_linkResults;
NetlinkList *l_addrResults;
if(!ifap)
{
return -1;
}
*ifap = NULL;
l_socket = netlink_socket();
if(l_socket < 0)
{
return -1;
}
l_linkResults = getResultList(l_socket, RTM_GETLINK);
if(!l_linkResults)
{
close(l_socket);
return -1;
}
l_addrResults = getResultList(l_socket, RTM_GETADDR);
if(!l_addrResults)
{
close(l_socket);
freeResultList(l_linkResults);
return -1;
}
l_result = 0;
l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
{
l_result = -1;
}
freeResultList(l_linkResults);
freeResultList(l_addrResults);
close(l_socket);
return l_result;
}
void freeifaddrs(struct ifaddrs *ifa)
{
struct ifaddrs *l_cur;
while(ifa)
{
l_cur = ifa;
ifa = ifa->ifa_next;
uv__free(l_cur);
}
}
@@ -0,0 +1,269 @@
/* 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.
*/
/* This file contains both the uv__async internal infrastructure and the
* user-facing uv_async_t functions.
*/
#include "uv.h"
#include "internal.h"
#include "atomic-ops.h"
#include <errno.h>
#include <stdio.h> /* snprintf() */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void uv__async_send(uv_loop_t* loop);
static int uv__async_start(uv_loop_t* loop);
static int uv__async_eventfd(void);
int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
int err;
err = uv__async_start(loop);
if (err)
return err;
uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
handle->async_cb = async_cb;
handle->pending = 0;
QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue);
uv__handle_start(handle);
return 0;
}
int uv_async_send(uv_async_t* handle) {
/* Do a cheap read first. */
if (ACCESS_ONCE(int, handle->pending) != 0)
return 0;
if (cmpxchgi(&handle->pending, 0, 1) == 0)
uv__async_send(handle->loop);
return 0;
}
void uv__async_close(uv_async_t* handle) {
QUEUE_REMOVE(&handle->queue);
uv__handle_stop(handle);
}
static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
char buf[1024];
ssize_t r;
QUEUE queue;
QUEUE* q;
uv_async_t* h;
assert(w == &loop->async_io_watcher);
for (;;) {
r = read(w->fd, buf, sizeof(buf));
if (r == sizeof(buf))
continue;
if (r != -1)
break;
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
if (errno == EINTR)
continue;
abort();
}
QUEUE_MOVE(&loop->async_handles, &queue);
while (!QUEUE_EMPTY(&queue)) {
q = QUEUE_HEAD(&queue);
h = QUEUE_DATA(q, uv_async_t, queue);
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&loop->async_handles, q);
if (cmpxchgi(&h->pending, 1, 0) == 0)
continue;
if (h->async_cb == NULL)
continue;
h->async_cb(h);
}
}
static void uv__async_send(uv_loop_t* loop) {
const void* buf;
ssize_t len;
int fd;
int r;
buf = "";
len = 1;
fd = loop->async_wfd;
#if defined(__linux__)
if (fd == -1) {
static const uint64_t val = 1;
buf = &val;
len = sizeof(val);
fd = loop->async_io_watcher.fd; /* eventfd */
}
#endif
do
r = write(fd, buf, len);
while (r == -1 && errno == EINTR);
if (r == len)
return;
if (r == -1)
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
abort();
}
static int uv__async_start(uv_loop_t* loop) {
int pipefd[2];
int err;
if (loop->async_io_watcher.fd != -1)
return 0;
err = uv__async_eventfd();
if (err >= 0) {
pipefd[0] = err;
pipefd[1] = -1;
}
else if (err == -ENOSYS) {
err = uv__make_pipe(pipefd, UV__F_NONBLOCK);
#if defined(__linux__)
/* Save a file descriptor by opening one of the pipe descriptors as
* read/write through the procfs. That file descriptor can then
* function as both ends of the pipe.
*/
if (err == 0) {
char buf[32];
int fd;
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]);
fd = uv__open_cloexec(buf, O_RDWR);
if (fd >= 0) {
uv__close(pipefd[0]);
uv__close(pipefd[1]);
pipefd[0] = fd;
pipefd[1] = fd;
}
}
#endif
}
if (err < 0)
return err;
uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]);
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
loop->async_wfd = pipefd[1];
return 0;
}
int uv__async_fork(uv_loop_t* loop) {
if (loop->async_io_watcher.fd == -1) /* never started */
return 0;
uv__async_stop(loop);
return uv__async_start(loop);
}
void uv__async_stop(uv_loop_t* loop) {
if (loop->async_io_watcher.fd == -1)
return;
if (loop->async_wfd != -1) {
if (loop->async_wfd != loop->async_io_watcher.fd)
uv__close(loop->async_wfd);
loop->async_wfd = -1;
}
uv__io_stop(loop, &loop->async_io_watcher, POLLIN);
uv__close(loop->async_io_watcher.fd);
loop->async_io_watcher.fd = -1;
}
static int uv__async_eventfd(void) {
#if defined(__linux__)
static int no_eventfd2;
static int no_eventfd;
int fd;
if (no_eventfd2)
goto skip_eventfd2;
fd = uv__eventfd2(0, UV__EFD_CLOEXEC | UV__EFD_NONBLOCK);
if (fd != -1)
return fd;
if (errno != ENOSYS)
return -errno;
no_eventfd2 = 1;
skip_eventfd2:
if (no_eventfd)
goto skip_eventfd;
fd = uv__eventfd(0);
if (fd != -1) {
uv__cloexec(fd, 1);
uv__nonblock(fd, 1);
return fd;
}
if (errno != ENOSYS)
return -errno;
no_eventfd = 1;
skip_eventfd:
#endif
return -ENOSYS;
}
@@ -0,0 +1,97 @@
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or 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.
*/
#ifndef UV_ATOMIC_OPS_H_
#define UV_ATOMIC_OPS_H_
#include "internal.h" /* UV_UNUSED */
#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
#include <atomic.h>
#define __sync_val_compare_and_swap(p, o, n) atomic_cas_ptr(p, o, n)
#endif
UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval));
UV_UNUSED(static void cpu_relax(void));
/* Prefer hand-rolled assembly over the gcc builtins because the latter also
* issue full memory barriers.
*/
UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
#if defined(__i386__) || defined(__x86_64__)
int out;
__asm__ __volatile__ ("lock; cmpxchg %2, %1;"
: "=a" (out), "+m" (*(volatile int*) ptr)
: "r" (newval), "0" (oldval)
: "memory");
return out;
#elif defined(_AIX) && defined(__xlC__)
const int out = (*(volatile int*) ptr);
__compare_and_swap(ptr, &oldval, newval);
return out;
#elif defined(__MVS__)
unsigned int op4;
if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
(unsigned int*) ptr, *ptr, &op4))
return oldval;
else
return op4;
#else
return __sync_val_compare_and_swap(ptr, oldval, newval);
#endif
}
UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
#if defined(__i386__) || defined(__x86_64__)
long out;
__asm__ __volatile__ ("lock; cmpxchg %2, %1;"
: "=a" (out), "+m" (*(volatile long*) ptr)
: "r" (newval), "0" (oldval)
: "memory");
return out;
#elif defined(_AIX) && defined(__xlC__)
const long out = (*(volatile int*) ptr);
# if defined(__64BIT__)
__compare_and_swaplp(ptr, &oldval, newval);
# else
__compare_and_swap(ptr, &oldval, newval);
# endif /* if defined(__64BIT__) */
return out;
#elif defined (__MVS__)
#ifdef _LP64
unsigned long long op4;
if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval,
(unsigned long long*) ptr, *ptr, &op4))
#else
unsigned long op4;
if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
(unsigned int*) ptr, *ptr, &op4))
#endif
return oldval;
else
return op4;
#else
return __sync_val_compare_and_swap(ptr, oldval, newval);
#endif
}
UV_UNUSED(static void cpu_relax(void)) {
#if defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */
#endif
}
#endif /* UV_ATOMIC_OPS_H_ */
@@ -0,0 +1,146 @@
/* Copyright libuv project 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 "internal.h"
#include <errno.h>
#include <stddef.h>
#include <ifaddrs.h>
#include <net/if.h>
#if !defined(__CYGWIN__) && !defined(__MSYS__)
#include <net/if_dl.h>
#endif
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
return 1;
if (ent->ifa_addr == NULL)
return 1;
/*
* If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family`
* equals to `AF_LINK` or not. Otherwise, the result depends on the operation
* system with `AF_LINK` or `PF_INET`.
*/
if (exclude_type == UV__EXCLUDE_IFPHYS)
return (ent->ifa_addr->sa_family != AF_LINK);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
/*
* On BSD getifaddrs returns information related to the raw underlying
* devices. We're not interested in this information.
*/
if (ent->ifa_addr->sa_family == AF_LINK)
return 1;
#elif defined(__NetBSD__) || defined(__OpenBSD__)
if (ent->ifa_addr->sa_family != PF_INET)
return 1;
#endif
return 0;
}
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
struct ifaddrs* addrs;
struct ifaddrs* ent;
uv_interface_address_t* address;
int i;
if (getifaddrs(&addrs) != 0)
return -errno;
*count = 0;
/* Count the number of interfaces */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
continue;
(*count)++;
}
*addresses = uv__malloc(*count * sizeof(**addresses));
if (*addresses == NULL) {
freeifaddrs(addrs);
return -ENOMEM;
}
address = *addresses;
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
continue;
address->name = uv__strdup(ent->ifa_name);
if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
} else {
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
}
if (ent->ifa_netmask->sa_family == AF_INET6) {
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
} else {
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
}
address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
address++;
}
/* Fill in physical addresses for each interface */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
continue;
address = *addresses;
for (i = 0; i < *count; i++) {
if (strcmp(address->name, ent->ifa_name) == 0) {
#if defined(__CYGWIN__) || defined(__MSYS__)
memset(address->phys_addr, 0, sizeof(address->phys_addr));
#else
struct sockaddr_dl* sa_addr;
sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
#endif
}
address++;
}
}
freeifaddrs(addrs);
return 0;
}
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(addresses[i].name);
}
uv__free(addresses);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,54 @@
/* Copyright libuv project 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 "internal.h"
#include <sys/sysinfo.h>
#include <unistd.h>
int uv_uptime(double* uptime) {
struct sysinfo info;
if (sysinfo(&info) < 0)
return -errno;
*uptime = info.uptime;
return 0;
}
int uv_resident_set_memory(size_t* rss) {
/* FIXME: read /proc/meminfo? */
*rss = 0;
return UV_ENOSYS;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
/* FIXME: read /proc/stat? */
*cpu_infos = NULL;
*count = 0;
return UV_ENOSYS;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
(void)cpu_infos;
(void)count;
}
@@ -0,0 +1,206 @@
/* 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 <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <TargetConditionals.h>
#if !TARGET_OS_IPHONE
# include <CoreFoundation/CoreFoundation.h>
# include <ApplicationServices/ApplicationServices.h>
#endif
static int uv__pthread_setname_np(const char* name) {
int (*dynamic_pthread_setname_np)(const char* name);
char namebuf[64]; /* MAXTHREADNAMESIZE */
int err;
/* pthread_setname_np() first appeared in OS X 10.6 and iOS 3.2. */
*(void **)(&dynamic_pthread_setname_np) =
dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (dynamic_pthread_setname_np == NULL)
return -ENOSYS;
strncpy(namebuf, name, sizeof(namebuf) - 1);
namebuf[sizeof(namebuf) - 1] = '\0';
err = dynamic_pthread_setname_np(namebuf);
if (err)
return -err;
return 0;
}
int uv__set_process_title(const char* title) {
#if TARGET_OS_IPHONE
return uv__pthread_setname_np(title);
#else
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
const char*,
CFStringEncoding);
CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef);
void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef);
void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef);
CFTypeRef (*pLSGetCurrentApplicationASN)(void);
OSStatus (*pLSSetApplicationInformationItem)(int,
CFTypeRef,
CFStringRef,
CFStringRef,
CFDictionaryRef*);
void* application_services_handle;
void* core_foundation_handle;
CFBundleRef launch_services_bundle;
CFStringRef* display_name_key;
CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef);
CFBundleRef (*pCFBundleGetMainBundle)(void);
CFBundleRef hi_services_bundle;
OSStatus (*pSetApplicationIsDaemon)(int);
CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef);
void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t,
void*);
CFTypeRef asn;
int err;
err = -ENOENT;
application_services_handle = dlopen("/System/Library/Frameworks/"
"ApplicationServices.framework/"
"Versions/A/ApplicationServices",
RTLD_LAZY | RTLD_LOCAL);
core_foundation_handle = dlopen("/System/Library/Frameworks/"
"CoreFoundation.framework/"
"Versions/A/CoreFoundation",
RTLD_LAZY | RTLD_LOCAL);
if (application_services_handle == NULL || core_foundation_handle == NULL)
goto out;
*(void **)(&pCFStringCreateWithCString) =
dlsym(core_foundation_handle, "CFStringCreateWithCString");
*(void **)(&pCFBundleGetBundleWithIdentifier) =
dlsym(core_foundation_handle, "CFBundleGetBundleWithIdentifier");
*(void **)(&pCFBundleGetDataPointerForName) =
dlsym(core_foundation_handle, "CFBundleGetDataPointerForName");
*(void **)(&pCFBundleGetFunctionPointerForName) =
dlsym(core_foundation_handle, "CFBundleGetFunctionPointerForName");
if (pCFStringCreateWithCString == NULL ||
pCFBundleGetBundleWithIdentifier == NULL ||
pCFBundleGetDataPointerForName == NULL ||
pCFBundleGetFunctionPointerForName == NULL) {
goto out;
}
#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
launch_services_bundle =
pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices"));
if (launch_services_bundle == NULL)
goto out;
*(void **)(&pLSGetCurrentApplicationASN) =
pCFBundleGetFunctionPointerForName(launch_services_bundle,
S("_LSGetCurrentApplicationASN"));
if (pLSGetCurrentApplicationASN == NULL)
goto out;
*(void **)(&pLSSetApplicationInformationItem) =
pCFBundleGetFunctionPointerForName(launch_services_bundle,
S("_LSSetApplicationInformationItem"));
if (pLSSetApplicationInformationItem == NULL)
goto out;
display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle,
S("_kLSDisplayNameKey"));
if (display_name_key == NULL || *display_name_key == NULL)
goto out;
*(void **)(&pCFBundleGetInfoDictionary) = dlsym(core_foundation_handle,
"CFBundleGetInfoDictionary");
*(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle,
"CFBundleGetMainBundle");
if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL)
goto out;
/* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */
hi_services_bundle =
pCFBundleGetBundleWithIdentifier(S("com.apple.HIServices"));
err = -ENOENT;
if (hi_services_bundle == NULL)
goto out;
*(void **)(&pSetApplicationIsDaemon) = pCFBundleGetFunctionPointerForName(
hi_services_bundle,
S("SetApplicationIsDaemon"));
*(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName(
launch_services_bundle,
S("_LSApplicationCheckIn"));
*(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) =
pCFBundleGetFunctionPointerForName(
launch_services_bundle,
S("_LSSetApplicationLaunchServicesServerConnectionStatus"));
if (pSetApplicationIsDaemon == NULL ||
pLSApplicationCheckIn == NULL ||
pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) {
goto out;
}
if (pSetApplicationIsDaemon(1) != noErr)
goto out;
pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL);
/* Check into process manager?! */
pLSApplicationCheckIn(-2,
pCFBundleGetInfoDictionary(pCFBundleGetMainBundle()));
asn = pLSGetCurrentApplicationASN();
err = -EINVAL;
if (pLSSetApplicationInformationItem(-2, /* Magic value. */
asn,
*display_name_key,
S(title),
NULL) != noErr) {
goto out;
}
uv__pthread_setname_np(title); /* Don't care if it fails. */
err = 0;
out:
if (core_foundation_handle != NULL)
dlclose(core_foundation_handle);
if (application_services_handle != NULL)
dlclose(application_services_handle);
return err;
#endif /* !TARGET_OS_IPHONE */
}
@@ -0,0 +1,231 @@
/* 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 "internal.h"
#include <assert.h>
#include <stdint.h>
#include <errno.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <unistd.h> /* sysconf */
int uv__platform_loop_init(uv_loop_t* loop) {
loop->cf_state = NULL;
if (uv__kqueue_init(loop))
return -errno;
return 0;
}
void uv__platform_loop_delete(uv_loop_t* loop) {
uv__fsevents_loop_delete(loop);
}
uint64_t uv__hrtime(uv_clocktype_t type) {
static mach_timebase_info_data_t info;
if ((ACCESS_ONCE(uint32_t, info.numer) == 0 ||
ACCESS_ONCE(uint32_t, info.denom) == 0) &&
mach_timebase_info(&info) != KERN_SUCCESS)
abort();
return mach_absolute_time() * info.numer / info.denom;
}
int uv_exepath(char* buffer, size_t* size) {
/* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
char abspath[PATH_MAX * 2 + 1];
char exepath[PATH_MAX + 1];
uint32_t exepath_size;
size_t abspath_size;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
exepath_size = sizeof(exepath);
if (_NSGetExecutablePath(exepath, &exepath_size))
return -EIO;
if (realpath(exepath, abspath) != abspath)
return -errno;
abspath_size = strlen(abspath);
if (abspath_size == 0)
return -EIO;
*size -= 1;
if (*size > abspath_size)
*size = abspath_size;
memcpy(buffer, abspath, *size);
buffer[*size] = '\0';
return 0;
}
uint64_t uv_get_free_memory(void) {
vm_statistics_data_t info;
mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
if (host_statistics(mach_host_self(), HOST_VM_INFO,
(host_info_t)&info, &count) != KERN_SUCCESS) {
return -EINVAL; /* FIXME(bnoordhuis) Translate error. */
}
return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE);
}
uint64_t uv_get_total_memory(void) {
uint64_t info;
int which[] = {CTL_HW, HW_MEMSIZE};
size_t size = sizeof(info);
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
return (uint64_t) info;
}
void uv_loadavg(double avg[3]) {
struct loadavg info;
size_t size = sizeof(info);
int which[] = {CTL_VM, VM_LOADAVG};
if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
avg[0] = (double) info.ldavg[0] / info.fscale;
avg[1] = (double) info.ldavg[1] / info.fscale;
avg[2] = (double) info.ldavg[2] / info.fscale;
}
int uv_resident_set_memory(size_t* rss) {
mach_msg_type_number_t count;
task_basic_info_data_t info;
kern_return_t err;
count = TASK_BASIC_INFO_COUNT;
err = task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t) &info,
&count);
(void) &err;
/* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than
* KERN_SUCCESS implies a libuv bug.
*/
assert(err == KERN_SUCCESS);
*rss = info.resident_size;
return 0;
}
int uv_uptime(double* uptime) {
time_t now;
struct timeval info;
size_t size = sizeof(info);
static int which[] = {CTL_KERN, KERN_BOOTTIME};
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
now = time(NULL);
*uptime = now - info.tv_sec;
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
multiplier = ((uint64_t)1000L / ticks);
char model[512];
uint64_t cpuspeed;
size_t size;
unsigned int i;
natural_t numcpus;
mach_msg_type_number_t msg_type;
processor_cpu_load_info_data_t *info;
uv_cpu_info_t* cpu_info;
size = sizeof(model);
if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&
sysctlbyname("hw.model", &model, &size, NULL, 0)) {
return -errno;
}
size = sizeof(cpuspeed);
if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0))
return -errno;
if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
(processor_info_array_t*)&info,
&msg_type) != KERN_SUCCESS) {
return -EINVAL; /* FIXME(bnoordhuis) Translate error. */
}
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
if (!(*cpu_infos)) {
vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
return -ENOMEM;
}
*count = numcpus;
for (i = 0; i < numcpus; i++) {
cpu_info = &(*cpu_infos)[i];
cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier;
cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier;
cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier;
cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier;
cpu_info->cpu_times.irq = 0;
cpu_info->model = uv__strdup(model);
cpu_info->speed = cpuspeed/1000000;
}
vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
return 0;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(cpu_infos[i].model);
}
uv__free(cpu_infos);
}
@@ -0,0 +1,80 @@
/* 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 "internal.h"
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#include <locale.h>
static int uv__dlerror(uv_lib_t* lib);
int uv_dlopen(const char* filename, uv_lib_t* lib) {
dlerror(); /* Reset error status. */
lib->errmsg = NULL;
lib->handle = dlopen(filename, RTLD_LAZY);
return lib->handle ? 0 : uv__dlerror(lib);
}
void uv_dlclose(uv_lib_t* lib) {
uv__free(lib->errmsg);
lib->errmsg = NULL;
if (lib->handle) {
/* Ignore errors. No good way to signal them without leaking memory. */
dlclose(lib->handle);
lib->handle = NULL;
}
}
int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
dlerror(); /* Reset error status. */
*ptr = dlsym(lib->handle, name);
return uv__dlerror(lib);
}
const char* uv_dlerror(const uv_lib_t* lib) {
return lib->errmsg ? lib->errmsg : "no error";
}
static int uv__dlerror(uv_lib_t* lib) {
const char* errmsg;
uv__free(lib->errmsg);
errmsg = dlerror();
if (errmsg) {
lib->errmsg = uv__strdup(errmsg);
return -1;
}
else {
lib->errmsg = NULL;
return 0;
}
}
@@ -0,0 +1,346 @@
/* 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 "internal.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <kvm.h>
#include <paths.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <vm/vm_param.h> /* VM_LOADAVG */
#include <time.h>
#include <stdlib.h>
#include <unistd.h> /* sysconf */
#include <fcntl.h>
#ifndef CPUSTATES
# define CPUSTATES 5U
#endif
#ifndef CP_USER
# define CP_USER 0
# define CP_NICE 1
# define CP_SYS 2
# define CP_IDLE 3
# define CP_INTR 4
#endif
static char *process_title;
int uv__platform_loop_init(uv_loop_t* loop) {
return uv__kqueue_init(loop);
}
void uv__platform_loop_delete(uv_loop_t* loop) {
}
#ifdef __DragonFly__
int uv_exepath(char* buffer, size_t* size) {
char abspath[PATH_MAX * 2 + 1];
ssize_t abspath_size;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath));
if (abspath_size < 0)
return -errno;
assert(abspath_size > 0);
*size -= 1;
if (*size > abspath_size)
*size = abspath_size;
memcpy(buffer, abspath, *size);
buffer[*size] = '\0';
return 0;
}
#else
int uv_exepath(char* buffer, size_t* size) {
char abspath[PATH_MAX * 2 + 1];
int mib[4];
size_t abspath_size;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
abspath_size = sizeof abspath;
if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0))
return -errno;
assert(abspath_size > 0);
abspath_size -= 1;
*size -= 1;
if (*size > abspath_size)
*size = abspath_size;
memcpy(buffer, abspath, *size);
buffer[*size] = '\0';
return 0;
}
#endif
uint64_t uv_get_free_memory(void) {
int freecount;
size_t size = sizeof(freecount);
if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0))
return -errno;
return (uint64_t) freecount * sysconf(_SC_PAGESIZE);
}
uint64_t uv_get_total_memory(void) {
unsigned long info;
int which[] = {CTL_HW, HW_PHYSMEM};
size_t size = sizeof(info);
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
return (uint64_t) info;
}
void uv_loadavg(double avg[3]) {
struct loadavg info;
size_t size = sizeof(info);
int which[] = {CTL_VM, VM_LOADAVG};
if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
avg[0] = (double) info.ldavg[0] / info.fscale;
avg[1] = (double) info.ldavg[1] / info.fscale;
avg[2] = (double) info.ldavg[2] / info.fscale;
}
char** uv_setup_args(int argc, char** argv) {
process_title = argc ? uv__strdup(argv[0]) : NULL;
return argv;
}
int uv_set_process_title(const char* title) {
int oid[4];
uv__free(process_title);
process_title = uv__strdup(title);
oid[0] = CTL_KERN;
oid[1] = KERN_PROC;
oid[2] = KERN_PROC_ARGS;
oid[3] = getpid();
sysctl(oid,
ARRAY_SIZE(oid),
NULL,
NULL,
process_title,
strlen(process_title) + 1);
return 0;
}
int uv_get_process_title(char* buffer, size_t size) {
size_t len;
if (buffer == NULL || size == 0)
return -EINVAL;
if (process_title) {
len = strlen(process_title) + 1;
if (size < len)
return -ENOBUFS;
memcpy(buffer, process_title, len);
} else {
len = 0;
}
buffer[len] = '\0';
return 0;
}
int uv_resident_set_memory(size_t* rss) {
kvm_t *kd = NULL;
struct kinfo_proc *kinfo = NULL;
pid_t pid;
int nprocs;
size_t page_size = getpagesize();
pid = getpid();
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
if (kd == NULL) goto error;
kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs);
if (kinfo == NULL) goto error;
#ifdef __DragonFly__
*rss = kinfo->kp_vm_rssize * page_size;
#else
*rss = kinfo->ki_rssize * page_size;
#endif
kvm_close(kd);
return 0;
error:
if (kd) kvm_close(kd);
return -EPERM;
}
int uv_uptime(double* uptime) {
int r;
struct timespec sp;
r = clock_gettime(CLOCK_MONOTONIC, &sp);
if (r)
return -errno;
*uptime = sp.tv_sec;
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus,
cur = 0;
uv_cpu_info_t* cpu_info;
const char* maxcpus_key;
const char* cptimes_key;
char model[512];
long* cp_times;
int numcpus;
size_t size;
int i;
#if defined(__DragonFly__)
/* This is not quite correct but DragonFlyBSD doesn't seem to have anything
* comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total,
* not per CPU). At least this stops uv_cpu_info() from failing completely.
*/
maxcpus_key = "hw.ncpu";
cptimes_key = "kern.cp_time";
#else
maxcpus_key = "kern.smp.maxcpus";
cptimes_key = "kern.cp_times";
#endif
size = sizeof(model);
if (sysctlbyname("hw.model", &model, &size, NULL, 0))
return -errno;
size = sizeof(numcpus);
if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
return -errno;
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
if (!(*cpu_infos))
return -ENOMEM;
*count = numcpus;
size = sizeof(cpuspeed);
if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) {
uv__free(*cpu_infos);
return -errno;
}
/* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of
* ncpu.
*/
size = sizeof(maxcpus);
if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) {
uv__free(*cpu_infos);
return -errno;
}
size = maxcpus * CPUSTATES * sizeof(long);
cp_times = uv__malloc(size);
if (cp_times == NULL) {
uv__free(*cpu_infos);
return -ENOMEM;
}
if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) {
uv__free(cp_times);
uv__free(*cpu_infos);
return -errno;
}
for (i = 0; i < numcpus; i++) {
cpu_info = &(*cpu_infos)[i];
cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
cpu_info->model = uv__strdup(model);
cpu_info->speed = cpuspeed;
cur+=CPUSTATES;
}
uv__free(cp_times);
return 0;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(cpu_infos[i].model);
}
uv__free(cpu_infos);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,901 @@
/* 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 "internal.h"
#if TARGET_OS_IPHONE
/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
int uv__fsevents_init(uv_fs_event_t* handle) {
return 0;
}
int uv__fsevents_close(uv_fs_event_t* handle) {
return 0;
}
void uv__fsevents_loop_delete(uv_loop_t* loop) {
}
#else /* TARGET_OS_IPHONE */
#include <dlfcn.h>
#include <assert.h>
#include <stdlib.h>
#include <pthread.h>
#include <CoreFoundation/CFRunLoop.h>
#include <CoreServices/CoreServices.h>
/* These are macros to avoid "initializer element is not constant" errors
* with old versions of gcc.
*/
#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod | \
kFSEventStreamEventFlagItemModified | \
kFSEventStreamEventFlagItemInodeMetaMod | \
kFSEventStreamEventFlagItemChangeOwner | \
kFSEventStreamEventFlagItemXattrMod)
#define kFSEventsRenamed (kFSEventStreamEventFlagItemCreated | \
kFSEventStreamEventFlagItemRemoved | \
kFSEventStreamEventFlagItemRenamed)
#define kFSEventsSystem (kFSEventStreamEventFlagUserDropped | \
kFSEventStreamEventFlagKernelDropped | \
kFSEventStreamEventFlagEventIdsWrapped | \
kFSEventStreamEventFlagHistoryDone | \
kFSEventStreamEventFlagMount | \
kFSEventStreamEventFlagUnmount | \
kFSEventStreamEventFlagRootChanged)
typedef struct uv__fsevents_event_s uv__fsevents_event_t;
typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
enum uv__cf_loop_signal_type_e {
kUVCFLoopSignalRegular,
kUVCFLoopSignalClosing
};
typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
struct uv__cf_loop_signal_s {
QUEUE member;
uv_fs_event_t* handle;
uv__cf_loop_signal_type_t type;
};
struct uv__fsevents_event_s {
QUEUE member;
int events;
char path[1];
};
struct uv__cf_loop_state_s {
CFRunLoopRef loop;
CFRunLoopSourceRef signal_source;
int fsevent_need_reschedule;
FSEventStreamRef fsevent_stream;
uv_sem_t fsevent_sem;
uv_mutex_t fsevent_mutex;
void* fsevent_handles[2];
unsigned int fsevent_handle_count;
};
/* Forward declarations */
static void uv__cf_loop_cb(void* arg);
static void* uv__cf_loop_runner(void* arg);
static int uv__cf_loop_signal(uv_loop_t* loop,
uv_fs_event_t* handle,
uv__cf_loop_signal_type_t type);
/* Lazy-loaded by uv__fsevents_global_init(). */
static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
const void**,
CFIndex,
const CFArrayCallBacks*);
static void (*pCFRelease)(CFTypeRef);
static void (*pCFRunLoopAddSource)(CFRunLoopRef,
CFRunLoopSourceRef,
CFStringRef);
static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
CFRunLoopSourceRef,
CFStringRef);
static void (*pCFRunLoopRun)(void);
static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
CFIndex,
CFRunLoopSourceContext*);
static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
static void (*pCFRunLoopStop)(CFRunLoopRef);
static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
CFAllocatorRef,
const char*);
static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
static CFStringRef (*pkCFRunLoopDefaultMode);
static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
FSEventStreamCallback,
FSEventStreamContext*,
CFArrayRef,
FSEventStreamEventId,
CFTimeInterval,
FSEventStreamCreateFlags);
static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
static void (*pFSEventStreamRelease)(FSEventStreamRef);
static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
CFRunLoopRef,
CFStringRef);
static Boolean (*pFSEventStreamStart)(FSEventStreamRef);
static void (*pFSEventStreamStop)(FSEventStreamRef);
#define UV__FSEVENTS_PROCESS(handle, block) \
do { \
QUEUE events; \
QUEUE* q; \
uv__fsevents_event_t* event; \
int err; \
uv_mutex_lock(&(handle)->cf_mutex); \
/* Split-off all events and empty original queue */ \
QUEUE_MOVE(&(handle)->cf_events, &events); \
/* Get error (if any) and zero original one */ \
err = (handle)->cf_error; \
(handle)->cf_error = 0; \
uv_mutex_unlock(&(handle)->cf_mutex); \
/* Loop through events, deallocating each after processing */ \
while (!QUEUE_EMPTY(&events)) { \
q = QUEUE_HEAD(&events); \
event = QUEUE_DATA(q, uv__fsevents_event_t, member); \
QUEUE_REMOVE(q); \
/* NOTE: Checking uv__is_active() is required here, because handle \
* callback may close handle and invoking it after it will lead to \
* incorrect behaviour */ \
if (!uv__is_closing((handle)) && uv__is_active((handle))) \
block \
/* Free allocated data */ \
uv__free(event); \
} \
if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \
(handle)->cb((handle), NULL, 0, err); \
} while (0)
/* Runs in UV loop's thread, when there're events to report to handle */
static void uv__fsevents_cb(uv_async_t* cb) {
uv_fs_event_t* handle;
handle = cb->data;
UV__FSEVENTS_PROCESS(handle, {
handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
});
}
/* Runs in CF thread, pushed event into handle's event list */
static void uv__fsevents_push_event(uv_fs_event_t* handle,
QUEUE* events,
int err) {
assert(events != NULL || err != 0);
uv_mutex_lock(&handle->cf_mutex);
/* Concatenate two queues */
if (events != NULL)
QUEUE_ADD(&handle->cf_events, events);
/* Propagate error */
if (err != 0)
handle->cf_error = err;
uv_mutex_unlock(&handle->cf_mutex);
uv_async_send(handle->cf_cb);
}
/* Runs in CF thread, when there're events in FSEventStream */
static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
void* info,
size_t numEvents,
void* eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
size_t i;
int len;
char** paths;
char* path;
char* pos;
uv_fs_event_t* handle;
QUEUE* q;
uv_loop_t* loop;
uv__cf_loop_state_t* state;
uv__fsevents_event_t* event;
QUEUE head;
loop = info;
state = loop->cf_state;
assert(state != NULL);
paths = eventPaths;
/* For each handle */
uv_mutex_lock(&state->fsevent_mutex);
QUEUE_FOREACH(q, &state->fsevent_handles) {
handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
QUEUE_INIT(&head);
/* Process and filter out events */
for (i = 0; i < numEvents; i++) {
/* Ignore system events */
if (eventFlags[i] & kFSEventsSystem)
continue;
path = paths[i];
len = strlen(path);
/* Filter out paths that are outside handle's request */
if (strncmp(path, handle->realpath, handle->realpath_len) != 0)
continue;
if (handle->realpath_len > 1 || *handle->realpath != '/') {
path += handle->realpath_len;
len -= handle->realpath_len;
/* Skip forward slash */
if (*path != '\0') {
path++;
len--;
}
}
#ifdef MAC_OS_X_VERSION_10_7
/* Ignore events with path equal to directory itself */
if (len == 0)
continue;
#endif /* MAC_OS_X_VERSION_10_7 */
/* Do not emit events from subdirectories (without option set) */
if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != 0) {
pos = strchr(path + 1, '/');
if (pos != NULL)
continue;
}
#ifndef MAC_OS_X_VERSION_10_7
path = "";
len = 0;
#endif /* MAC_OS_X_VERSION_10_7 */
event = uv__malloc(sizeof(*event) + len);
if (event == NULL)
break;
memset(event, 0, sizeof(*event));
memcpy(event->path, path, len + 1);
if ((eventFlags[i] & kFSEventsModified) != 0 &&
(eventFlags[i] & kFSEventsRenamed) == 0)
event->events = UV_CHANGE;
else
event->events = UV_RENAME;
QUEUE_INSERT_TAIL(&head, &event->member);
}
if (!QUEUE_EMPTY(&head))
uv__fsevents_push_event(handle, &head, 0);
}
uv_mutex_unlock(&state->fsevent_mutex);
}
/* Runs in CF thread */
static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
uv__cf_loop_state_t* state;
FSEventStreamContext ctx;
FSEventStreamRef ref;
CFAbsoluteTime latency;
FSEventStreamCreateFlags flags;
/* Initialize context */
ctx.version = 0;
ctx.info = loop;
ctx.retain = NULL;
ctx.release = NULL;
ctx.copyDescription = NULL;
latency = 0.05;
/* Explanation of selected flags:
* 1. NoDefer - without this flag, events that are happening continuously
* (i.e. each event is happening after time interval less than `latency`,
* counted from previous event), will be deferred and passed to callback
* once they'll either fill whole OS buffer, or when this continuous stream
* will stop (i.e. there'll be delay between events, bigger than
* `latency`).
* Specifying this flag will invoke callback after `latency` time passed
* since event.
* 2. FileEvents - fire callback for file changes too (by default it is firing
* it only for directory changes).
*/
flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
/*
* NOTE: It might sound like a good idea to remember last seen StreamEventId,
* but in reality one dir might have last StreamEventId less than, the other,
* that is being watched now. Which will cause FSEventStream API to report
* changes to files from the past.
*/
ref = pFSEventStreamCreate(NULL,
&uv__fsevents_event_cb,
&ctx,
paths,
kFSEventStreamEventIdSinceNow,
latency,
flags);
assert(ref != NULL);
state = loop->cf_state;
pFSEventStreamScheduleWithRunLoop(ref,
state->loop,
*pkCFRunLoopDefaultMode);
if (!pFSEventStreamStart(ref)) {
pFSEventStreamInvalidate(ref);
pFSEventStreamRelease(ref);
return -EMFILE;
}
state->fsevent_stream = ref;
return 0;
}
/* Runs in CF thread */
static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
uv__cf_loop_state_t* state;
state = loop->cf_state;
if (state->fsevent_stream == NULL)
return;
/* Stop emitting events */
pFSEventStreamStop(state->fsevent_stream);
/* Release stream */
pFSEventStreamInvalidate(state->fsevent_stream);
pFSEventStreamRelease(state->fsevent_stream);
state->fsevent_stream = NULL;
}
/* Runs in CF thread, when there're new fsevent handles to add to stream */
static void uv__fsevents_reschedule(uv_fs_event_t* handle,
uv__cf_loop_signal_type_t type) {
uv__cf_loop_state_t* state;
QUEUE* q;
uv_fs_event_t* curr;
CFArrayRef cf_paths;
CFStringRef* paths;
unsigned int i;
int err;
unsigned int path_count;
state = handle->loop->cf_state;
paths = NULL;
cf_paths = NULL;
err = 0;
/* NOTE: `i` is used in deallocation loop below */
i = 0;
/* Optimization to prevent O(n^2) time spent when starting to watch
* many files simultaneously
*/
uv_mutex_lock(&state->fsevent_mutex);
if (state->fsevent_need_reschedule == 0) {
uv_mutex_unlock(&state->fsevent_mutex);
goto final;
}
state->fsevent_need_reschedule = 0;
uv_mutex_unlock(&state->fsevent_mutex);
/* Destroy previous FSEventStream */
uv__fsevents_destroy_stream(handle->loop);
/* Any failure below will be a memory failure */
err = -ENOMEM;
/* Create list of all watched paths */
uv_mutex_lock(&state->fsevent_mutex);
path_count = state->fsevent_handle_count;
if (path_count != 0) {
paths = uv__malloc(sizeof(*paths) * path_count);
if (paths == NULL) {
uv_mutex_unlock(&state->fsevent_mutex);
goto final;
}
q = &state->fsevent_handles;
for (; i < path_count; i++) {
q = QUEUE_NEXT(q);
assert(q != &state->fsevent_handles);
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
assert(curr->realpath != NULL);
paths[i] =
pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
if (paths[i] == NULL) {
uv_mutex_unlock(&state->fsevent_mutex);
goto final;
}
}
}
uv_mutex_unlock(&state->fsevent_mutex);
err = 0;
if (path_count != 0) {
/* Create new FSEventStream */
cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
if (cf_paths == NULL) {
err = -ENOMEM;
goto final;
}
err = uv__fsevents_create_stream(handle->loop, cf_paths);
}
final:
/* Deallocate all paths in case of failure */
if (err != 0) {
if (cf_paths == NULL) {
while (i != 0)
pCFRelease(paths[--i]);
uv__free(paths);
} else {
/* CFArray takes ownership of both strings and original C-array */
pCFRelease(cf_paths);
}
/* Broadcast error to all handles */
uv_mutex_lock(&state->fsevent_mutex);
QUEUE_FOREACH(q, &state->fsevent_handles) {
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
uv__fsevents_push_event(curr, NULL, err);
}
uv_mutex_unlock(&state->fsevent_mutex);
}
/*
* Main thread will block until the removal of handle from the list,
* we must tell it when we're ready.
*
* NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
*/
if (type == kUVCFLoopSignalClosing)
uv_sem_post(&state->fsevent_sem);
}
static int uv__fsevents_global_init(void) {
static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static void* core_foundation_handle;
static void* core_services_handle;
int err;
err = 0;
pthread_mutex_lock(&global_init_mutex);
if (core_foundation_handle != NULL)
goto out;
/* The libraries are never unloaded because we currently don't have a good
* mechanism for keeping a reference count. It's unlikely to be an issue
* but if it ever becomes one, we can turn the dynamic library handles into
* per-event loop properties and have the dynamic linker keep track for us.
*/
err = -ENOSYS;
core_foundation_handle = dlopen("/System/Library/Frameworks/"
"CoreFoundation.framework/"
"Versions/A/CoreFoundation",
RTLD_LAZY | RTLD_LOCAL);
if (core_foundation_handle == NULL)
goto out;
core_services_handle = dlopen("/System/Library/Frameworks/"
"CoreServices.framework/"
"Versions/A/CoreServices",
RTLD_LAZY | RTLD_LOCAL);
if (core_services_handle == NULL)
goto out;
err = -ENOENT;
#define V(handle, symbol) \
do { \
*(void **)(&p ## symbol) = dlsym((handle), #symbol); \
if (p ## symbol == NULL) \
goto out; \
} \
while (0)
V(core_foundation_handle, CFArrayCreate);
V(core_foundation_handle, CFRelease);
V(core_foundation_handle, CFRunLoopAddSource);
V(core_foundation_handle, CFRunLoopGetCurrent);
V(core_foundation_handle, CFRunLoopRemoveSource);
V(core_foundation_handle, CFRunLoopRun);
V(core_foundation_handle, CFRunLoopSourceCreate);
V(core_foundation_handle, CFRunLoopSourceSignal);
V(core_foundation_handle, CFRunLoopStop);
V(core_foundation_handle, CFRunLoopWakeUp);
V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
V(core_foundation_handle, CFStringGetSystemEncoding);
V(core_foundation_handle, kCFRunLoopDefaultMode);
V(core_services_handle, FSEventStreamCreate);
V(core_services_handle, FSEventStreamFlushSync);
V(core_services_handle, FSEventStreamInvalidate);
V(core_services_handle, FSEventStreamRelease);
V(core_services_handle, FSEventStreamScheduleWithRunLoop);
V(core_services_handle, FSEventStreamStart);
V(core_services_handle, FSEventStreamStop);
#undef V
err = 0;
out:
if (err && core_services_handle != NULL) {
dlclose(core_services_handle);
core_services_handle = NULL;
}
if (err && core_foundation_handle != NULL) {
dlclose(core_foundation_handle);
core_foundation_handle = NULL;
}
pthread_mutex_unlock(&global_init_mutex);
return err;
}
/* Runs in UV loop */
static int uv__fsevents_loop_init(uv_loop_t* loop) {
CFRunLoopSourceContext ctx;
uv__cf_loop_state_t* state;
pthread_attr_t attr_storage;
pthread_attr_t* attr;
int err;
if (loop->cf_state != NULL)
return 0;
err = uv__fsevents_global_init();
if (err)
return err;
state = uv__calloc(1, sizeof(*state));
if (state == NULL)
return -ENOMEM;
err = uv_mutex_init(&loop->cf_mutex);
if (err)
goto fail_mutex_init;
err = uv_sem_init(&loop->cf_sem, 0);
if (err)
goto fail_sem_init;
QUEUE_INIT(&loop->cf_signals);
err = uv_sem_init(&state->fsevent_sem, 0);
if (err)
goto fail_fsevent_sem_init;
err = uv_mutex_init(&state->fsevent_mutex);
if (err)
goto fail_fsevent_mutex_init;
QUEUE_INIT(&state->fsevent_handles);
state->fsevent_need_reschedule = 0;
state->fsevent_handle_count = 0;
memset(&ctx, 0, sizeof(ctx));
ctx.info = loop;
ctx.perform = uv__cf_loop_cb;
state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
if (state->signal_source == NULL) {
err = -ENOMEM;
goto fail_signal_source_create;
}
/* In the unlikely event that pthread_attr_init() fails, create the thread
* with the default stack size. We'll use a little more address space but
* that in itself is not a fatal error.
*/
attr = &attr_storage;
if (pthread_attr_init(attr))
attr = NULL;
if (attr != NULL)
if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN))
abort();
loop->cf_state = state;
/* uv_thread_t is an alias for pthread_t. */
err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop);
if (attr != NULL)
pthread_attr_destroy(attr);
if (err)
goto fail_thread_create;
/* Synchronize threads */
uv_sem_wait(&loop->cf_sem);
return 0;
fail_thread_create:
loop->cf_state = NULL;
fail_signal_source_create:
uv_mutex_destroy(&state->fsevent_mutex);
fail_fsevent_mutex_init:
uv_sem_destroy(&state->fsevent_sem);
fail_fsevent_sem_init:
uv_sem_destroy(&loop->cf_sem);
fail_sem_init:
uv_mutex_destroy(&loop->cf_mutex);
fail_mutex_init:
uv__free(state);
return err;
}
/* Runs in UV loop */
void uv__fsevents_loop_delete(uv_loop_t* loop) {
uv__cf_loop_signal_t* s;
uv__cf_loop_state_t* state;
QUEUE* q;
if (loop->cf_state == NULL)
return;
if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
abort();
uv_thread_join(&loop->cf_thread);
uv_sem_destroy(&loop->cf_sem);
uv_mutex_destroy(&loop->cf_mutex);
/* Free any remaining data */
while (!QUEUE_EMPTY(&loop->cf_signals)) {
q = QUEUE_HEAD(&loop->cf_signals);
s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
QUEUE_REMOVE(q);
uv__free(s);
}
/* Destroy state */
state = loop->cf_state;
uv_sem_destroy(&state->fsevent_sem);
uv_mutex_destroy(&state->fsevent_mutex);
pCFRelease(state->signal_source);
uv__free(state);
loop->cf_state = NULL;
}
/* Runs in CF thread. This is the CF loop's body */
static void* uv__cf_loop_runner(void* arg) {
uv_loop_t* loop;
uv__cf_loop_state_t* state;
loop = arg;
state = loop->cf_state;
state->loop = pCFRunLoopGetCurrent();
pCFRunLoopAddSource(state->loop,
state->signal_source,
*pkCFRunLoopDefaultMode);
uv_sem_post(&loop->cf_sem);
pCFRunLoopRun();
pCFRunLoopRemoveSource(state->loop,
state->signal_source,
*pkCFRunLoopDefaultMode);
return NULL;
}
/* Runs in CF thread, executed after `uv__cf_loop_signal()` */
static void uv__cf_loop_cb(void* arg) {
uv_loop_t* loop;
uv__cf_loop_state_t* state;
QUEUE* item;
QUEUE split_head;
uv__cf_loop_signal_t* s;
loop = arg;
state = loop->cf_state;
uv_mutex_lock(&loop->cf_mutex);
QUEUE_MOVE(&loop->cf_signals, &split_head);
uv_mutex_unlock(&loop->cf_mutex);
while (!QUEUE_EMPTY(&split_head)) {
item = QUEUE_HEAD(&split_head);
QUEUE_REMOVE(item);
s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
/* This was a termination signal */
if (s->handle == NULL)
pCFRunLoopStop(state->loop);
else
uv__fsevents_reschedule(s->handle, s->type);
uv__free(s);
}
}
/* Runs in UV loop to notify CF thread */
int uv__cf_loop_signal(uv_loop_t* loop,
uv_fs_event_t* handle,
uv__cf_loop_signal_type_t type) {
uv__cf_loop_signal_t* item;
uv__cf_loop_state_t* state;
item = uv__malloc(sizeof(*item));
if (item == NULL)
return -ENOMEM;
item->handle = handle;
item->type = type;
uv_mutex_lock(&loop->cf_mutex);
QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
uv_mutex_unlock(&loop->cf_mutex);
state = loop->cf_state;
assert(state != NULL);
pCFRunLoopSourceSignal(state->signal_source);
pCFRunLoopWakeUp(state->loop);
return 0;
}
/* Runs in UV loop to initialize handle */
int uv__fsevents_init(uv_fs_event_t* handle) {
int err;
uv__cf_loop_state_t* state;
err = uv__fsevents_loop_init(handle->loop);
if (err)
return err;
/* Get absolute path to file */
handle->realpath = realpath(handle->path, NULL);
if (handle->realpath == NULL)
return -errno;
handle->realpath_len = strlen(handle->realpath);
/* Initialize event queue */
QUEUE_INIT(&handle->cf_events);
handle->cf_error = 0;
/*
* Events will occur in other thread.
* Initialize callback for getting them back into event loop's thread
*/
handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
if (handle->cf_cb == NULL) {
err = -ENOMEM;
goto fail_cf_cb_malloc;
}
handle->cf_cb->data = handle;
uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
uv_unref((uv_handle_t*) handle->cf_cb);
err = uv_mutex_init(&handle->cf_mutex);
if (err)
goto fail_cf_mutex_init;
/* Insert handle into the list */
state = handle->loop->cf_state;
uv_mutex_lock(&state->fsevent_mutex);
QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
state->fsevent_handle_count++;
state->fsevent_need_reschedule = 1;
uv_mutex_unlock(&state->fsevent_mutex);
/* Reschedule FSEventStream */
assert(handle != NULL);
err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
if (err)
goto fail_loop_signal;
return 0;
fail_loop_signal:
uv_mutex_destroy(&handle->cf_mutex);
fail_cf_mutex_init:
uv__free(handle->cf_cb);
handle->cf_cb = NULL;
fail_cf_cb_malloc:
uv__free(handle->realpath);
handle->realpath = NULL;
handle->realpath_len = 0;
return err;
}
/* Runs in UV loop to de-initialize handle */
int uv__fsevents_close(uv_fs_event_t* handle) {
int err;
uv__cf_loop_state_t* state;
if (handle->cf_cb == NULL)
return -EINVAL;
/* Remove handle from the list */
state = handle->loop->cf_state;
uv_mutex_lock(&state->fsevent_mutex);
QUEUE_REMOVE(&handle->cf_member);
state->fsevent_handle_count--;
state->fsevent_need_reschedule = 1;
uv_mutex_unlock(&state->fsevent_mutex);
/* Reschedule FSEventStream */
assert(handle != NULL);
err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing);
if (err)
return -err;
/* Wait for deinitialization */
uv_sem_wait(&state->fsevent_sem);
uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
handle->cf_cb = NULL;
/* Free data in queue */
UV__FSEVENTS_PROCESS(handle, {
/* NOP */
});
uv_mutex_destroy(&handle->cf_mutex);
uv__free(handle->realpath);
handle->realpath = NULL;
handle->realpath_len = 0;
return 0;
}
#endif /* TARGET_OS_IPHONE */
@@ -0,0 +1,202 @@
/* 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.
*/
/* Expose glibc-specific EAI_* error codes. Needs to be defined before we
* include any headers.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include "uv.h"
#include "internal.h"
#include <errno.h>
#include <stddef.h> /* NULL */
#include <stdlib.h>
#include <string.h>
/* EAI_* constants. */
#include <netdb.h>
int uv__getaddrinfo_translate_error(int sys_err) {
switch (sys_err) {
case 0: return 0;
#if defined(EAI_ADDRFAMILY)
case EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY;
#endif
#if defined(EAI_AGAIN)
case EAI_AGAIN: return UV_EAI_AGAIN;
#endif
#if defined(EAI_BADFLAGS)
case EAI_BADFLAGS: return UV_EAI_BADFLAGS;
#endif
#if defined(EAI_BADHINTS)
case EAI_BADHINTS: return UV_EAI_BADHINTS;
#endif
#if defined(EAI_CANCELED)
case EAI_CANCELED: return UV_EAI_CANCELED;
#endif
#if defined(EAI_FAIL)
case EAI_FAIL: return UV_EAI_FAIL;
#endif
#if defined(EAI_FAMILY)
case EAI_FAMILY: return UV_EAI_FAMILY;
#endif
#if defined(EAI_MEMORY)
case EAI_MEMORY: return UV_EAI_MEMORY;
#endif
#if defined(EAI_NODATA)
case EAI_NODATA: return UV_EAI_NODATA;
#endif
#if defined(EAI_NONAME)
# if !defined(EAI_NODATA) || EAI_NODATA != EAI_NONAME
case EAI_NONAME: return UV_EAI_NONAME;
# endif
#endif
#if defined(EAI_OVERFLOW)
case EAI_OVERFLOW: return UV_EAI_OVERFLOW;
#endif
#if defined(EAI_PROTOCOL)
case EAI_PROTOCOL: return UV_EAI_PROTOCOL;
#endif
#if defined(EAI_SERVICE)
case EAI_SERVICE: return UV_EAI_SERVICE;
#endif
#if defined(EAI_SOCKTYPE)
case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE;
#endif
#if defined(EAI_SYSTEM)
case EAI_SYSTEM: return -errno;
#endif
}
assert(!"unknown EAI_* error code");
abort();
return 0; /* Pacify compiler. */
}
static void uv__getaddrinfo_work(struct uv__work* w) {
uv_getaddrinfo_t* req;
int err;
req = container_of(w, uv_getaddrinfo_t, work_req);
err = getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo);
req->retcode = uv__getaddrinfo_translate_error(err);
}
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
uv_getaddrinfo_t* req;
req = container_of(w, uv_getaddrinfo_t, work_req);
uv__req_unregister(req->loop, req);
/* See initialization in uv_getaddrinfo(). */
if (req->hints)
uv__free(req->hints);
else if (req->service)
uv__free(req->service);
else if (req->hostname)
uv__free(req->hostname);
else
assert(0);
req->hints = NULL;
req->service = NULL;
req->hostname = NULL;
if (status == -ECANCELED) {
assert(req->retcode == 0);
req->retcode = UV_EAI_CANCELED;
}
if (req->cb)
req->cb(req, req->retcode, req->addrinfo);
}
int uv_getaddrinfo(uv_loop_t* loop,
uv_getaddrinfo_t* req,
uv_getaddrinfo_cb cb,
const char* hostname,
const char* service,
const struct addrinfo* hints) {
size_t hostname_len;
size_t service_len;
size_t hints_len;
size_t len;
char* buf;
if (req == NULL || (hostname == NULL && service == NULL))
return -EINVAL;
hostname_len = hostname ? strlen(hostname) + 1 : 0;
service_len = service ? strlen(service) + 1 : 0;
hints_len = hints ? sizeof(*hints) : 0;
buf = uv__malloc(hostname_len + service_len + hints_len);
if (buf == NULL)
return -ENOMEM;
uv__req_init(loop, req, UV_GETADDRINFO);
req->loop = loop;
req->cb = cb;
req->addrinfo = NULL;
req->hints = NULL;
req->service = NULL;
req->hostname = NULL;
req->retcode = 0;
/* order matters, see uv_getaddrinfo_done() */
len = 0;
if (hints) {
req->hints = memcpy(buf + len, hints, sizeof(*hints));
len += sizeof(*hints);
}
if (service) {
req->service = memcpy(buf + len, service, service_len);
len += service_len;
}
if (hostname)
req->hostname = memcpy(buf + len, hostname, hostname_len);
if (cb) {
uv__work_submit(loop,
&req->work_req,
uv__getaddrinfo_work,
uv__getaddrinfo_done);
return 0;
} else {
uv__getaddrinfo_work(&req->work_req);
uv__getaddrinfo_done(&req->work_req, 0);
return req->retcode;
}
}
void uv_freeaddrinfo(struct addrinfo* ai) {
if (ai)
freeaddrinfo(ai);
}
@@ -0,0 +1,120 @@
/* 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 <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "uv.h"
#include "internal.h"
static void uv__getnameinfo_work(struct uv__work* w) {
uv_getnameinfo_t* req;
int err;
socklen_t salen;
req = container_of(w, uv_getnameinfo_t, work_req);
if (req->storage.ss_family == AF_INET)
salen = sizeof(struct sockaddr_in);
else if (req->storage.ss_family == AF_INET6)
salen = sizeof(struct sockaddr_in6);
else
abort();
err = getnameinfo((struct sockaddr*) &req->storage,
salen,
req->host,
sizeof(req->host),
req->service,
sizeof(req->service),
req->flags);
req->retcode = uv__getaddrinfo_translate_error(err);
}
static void uv__getnameinfo_done(struct uv__work* w, int status) {
uv_getnameinfo_t* req;
char* host;
char* service;
req = container_of(w, uv_getnameinfo_t, work_req);
uv__req_unregister(req->loop, req);
host = service = NULL;
if (status == -ECANCELED) {
assert(req->retcode == 0);
req->retcode = UV_EAI_CANCELED;
} else if (req->retcode == 0) {
host = req->host;
service = req->service;
}
if (req->getnameinfo_cb)
req->getnameinfo_cb(req, req->retcode, host, service);
}
/*
* Entry point for getnameinfo
* return 0 if a callback will be made
* return error code if validation fails
*/
int uv_getnameinfo(uv_loop_t* loop,
uv_getnameinfo_t* req,
uv_getnameinfo_cb getnameinfo_cb,
const struct sockaddr* addr,
int flags) {
if (req == NULL || addr == NULL)
return UV_EINVAL;
if (addr->sa_family == AF_INET) {
memcpy(&req->storage,
addr,
sizeof(struct sockaddr_in));
} else if (addr->sa_family == AF_INET6) {
memcpy(&req->storage,
addr,
sizeof(struct sockaddr_in6));
} else {
return UV_EINVAL;
}
uv__req_init(loop, (uv_req_t*)req, UV_GETNAMEINFO);
req->getnameinfo_cb = getnameinfo_cb;
req->flags = flags;
req->type = UV_GETNAMEINFO;
req->loop = loop;
req->retcode = 0;
if (getnameinfo_cb) {
uv__work_submit(loop,
&req->work_req,
uv__getnameinfo_work,
uv__getnameinfo_done);
return 0;
} else {
uv__getnameinfo_work(&req->work_req);
uv__getnameinfo_done(&req->work_req, 0);
return req->retcode;
}
}
@@ -0,0 +1,338 @@
/* 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.
*/
#ifndef UV_UNIX_INTERNAL_H_
#define UV_UNIX_INTERNAL_H_
#include "uv-common.h"
#include <assert.h>
#include <stdlib.h> /* abort */
#include <string.h> /* strrchr */
#include <fcntl.h> /* O_CLOEXEC, may be */
#include <stdio.h>
#if defined(__STRICT_ANSI__)
# define inline __inline
#endif
#if defined(__linux__)
# include "linux-syscalls.h"
#endif /* __linux__ */
#if defined(__MVS__)
# include "os390-syscalls.h"
#endif /* __MVS__ */
#if defined(__sun)
# include <sys/port.h>
# include <port.h>
#endif /* __sun */
#if defined(_AIX)
# define reqevents events
# define rtnevents revents
# include <sys/poll.h>
#else
# include <poll.h>
#endif /* _AIX */
#if defined(__APPLE__) && !TARGET_OS_IPHONE
# include <AvailabilityMacros.h>
#endif
#if defined(__ANDROID__)
int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
# ifdef pthread_sigmask
# undef pthread_sigmask
# endif
# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset)
#endif
#define ACCESS_ONCE(type, var) \
(*(volatile type*) &(var))
#define ROUND_UP(a, b) \
((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a))
#define UNREACHABLE() \
do { \
assert(0 && "unreachable code"); \
abort(); \
} \
while (0)
#define SAVE_ERRNO(block) \
do { \
int _saved_errno = errno; \
do { block; } while (0); \
errno = _saved_errno; \
} \
while (0)
/* The __clang__ and __INTEL_COMPILER checks are superfluous because they
* define __GNUC__. They are here to convey to you, dear reader, that these
* macros are enabled when compiling with clang or icc.
*/
#if defined(__clang__) || \
defined(__GNUC__) || \
defined(__INTEL_COMPILER) || \
defined(__SUNPRO_C)
# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration
# define UV_UNUSED(declaration) __attribute__((unused)) declaration
#else
# define UV_DESTRUCTOR(declaration) declaration
# define UV_UNUSED(declaration) declaration
#endif
/* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */
#ifdef POLLRDHUP
# define UV__POLLRDHUP POLLRDHUP
#else
# define UV__POLLRDHUP 0x2000
#endif
#ifdef POLLPRI
# define UV__POLLPRI POLLPRI
#else
# define UV__POLLPRI 0
#endif
#if !defined(O_CLOEXEC) && defined(__FreeBSD__)
/*
* It may be that we are just missing `__POSIX_VISIBLE >= 200809`.
* Try using fixed value const and give up, if it doesn't work
*/
# define O_CLOEXEC 0x00100000
#endif
typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
/* handle flags */
enum {
UV_CLOSING = 0x01, /* uv_close() called but not finished. */
UV_CLOSED = 0x02, /* close(2) finished. */
UV_STREAM_READING = 0x04, /* uv_read_start() called. */
UV_STREAM_SHUTTING = 0x08, /* uv_shutdown() called but not complete. */
UV_STREAM_SHUT = 0x10, /* Write side closed. */
UV_STREAM_READABLE = 0x20, /* The stream is readable */
UV_STREAM_WRITABLE = 0x40, /* The stream is writable */
UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */
UV_STREAM_READ_PARTIAL = 0x100, /* read(2) read less than requested. */
UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */
UV_TCP_NODELAY = 0x400, /* Disable Nagle. */
UV_TCP_KEEPALIVE = 0x800, /* Turn on keep-alive. */
UV_TCP_SINGLE_ACCEPT = 0x1000, /* Only accept() when idle. */
UV_HANDLE_IPV6 = 0x10000, /* Handle is bound to a IPv6 socket. */
UV_UDP_PROCESSING = 0x20000, /* Handle is running the send callback queue. */
UV_HANDLE_BOUND = 0x40000 /* Handle is bound to an address and port */
};
/* loop flags */
enum {
UV_LOOP_BLOCK_SIGPROF = 1
};
/* flags of excluding ifaddr */
enum {
UV__EXCLUDE_IFPHYS,
UV__EXCLUDE_IFADDR
};
typedef enum {
UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */
UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */
} uv_clocktype_t;
struct uv__stream_queued_fds_s {
unsigned int size;
unsigned int offset;
int fds[1];
};
#if defined(_AIX) || \
defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || \
defined(__linux__) || \
defined(__OpenBSD__)
#define uv__cloexec uv__cloexec_ioctl
#define uv__nonblock uv__nonblock_ioctl
#else
#define uv__cloexec uv__cloexec_fcntl
#define uv__nonblock uv__nonblock_fcntl
#endif
/* core */
int uv__cloexec_ioctl(int fd, int set);
int uv__cloexec_fcntl(int fd, int set);
int uv__nonblock_ioctl(int fd, int set);
int uv__nonblock_fcntl(int fd, int set);
int uv__close(int fd);
int uv__close_nocheckstdio(int fd);
int uv__socket(int domain, int type, int protocol);
int uv__dup(int fd);
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
void uv__make_close_pending(uv_handle_t* handle);
int uv__getiovmax(void);
void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);
void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events);
void uv__io_close(uv_loop_t* loop, uv__io_t* w);
void uv__io_feed(uv_loop_t* loop, uv__io_t* w);
int uv__io_active(const uv__io_t* w, unsigned int events);
int uv__io_check_fd(uv_loop_t* loop, int fd);
void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */
int uv__io_fork(uv_loop_t* loop);
/* async */
void uv__async_stop(uv_loop_t* loop);
int uv__async_fork(uv_loop_t* loop);
/* loop */
void uv__run_idle(uv_loop_t* loop);
void uv__run_check(uv_loop_t* loop);
void uv__run_prepare(uv_loop_t* loop);
/* stream */
void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream,
uv_handle_type type);
int uv__stream_open(uv_stream_t*, int fd, int flags);
void uv__stream_destroy(uv_stream_t* stream);
#if defined(__APPLE__)
int uv__stream_try_select(uv_stream_t* stream, int* fd);
#endif /* defined(__APPLE__) */
void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
int uv__accept(int sockfd);
int uv__dup2_cloexec(int oldfd, int newfd);
int uv__open_cloexec(const char* path, int flags);
/* tcp */
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
int uv__tcp_nodelay(int fd, int on);
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
/* pipe */
int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
/* timer */
void uv__run_timers(uv_loop_t* loop);
int uv__next_timeout(const uv_loop_t* loop);
/* signal */
void uv__signal_close(uv_signal_t* handle);
void uv__signal_global_once_init(void);
void uv__signal_loop_cleanup(uv_loop_t* loop);
int uv__signal_loop_fork(uv_loop_t* loop);
/* platform specific */
uint64_t uv__hrtime(uv_clocktype_t type);
int uv__kqueue_init(uv_loop_t* loop);
int uv__platform_loop_init(uv_loop_t* loop);
void uv__platform_loop_delete(uv_loop_t* loop);
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd);
/* various */
void uv__async_close(uv_async_t* handle);
void uv__check_close(uv_check_t* handle);
void uv__fs_event_close(uv_fs_event_t* handle);
void uv__idle_close(uv_idle_t* handle);
void uv__pipe_close(uv_pipe_t* handle);
void uv__poll_close(uv_poll_t* handle);
void uv__prepare_close(uv_prepare_t* handle);
void uv__process_close(uv_process_t* handle);
void uv__stream_close(uv_stream_t* handle);
void uv__tcp_close(uv_tcp_t* handle);
void uv__timer_close(uv_timer_t* handle);
void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle);
uv_handle_type uv__handle_type(int fd);
FILE* uv__open_file(const char* path);
int uv__getpwuid_r(uv_passwd_t* pwd);
#if defined(__APPLE__)
int uv___stream_fd(const uv_stream_t* handle);
#define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle)))
#else
#define uv__stream_fd(handle) ((handle)->io_watcher.fd)
#endif /* defined(__APPLE__) */
#ifdef UV__O_NONBLOCK
# define UV__F_NONBLOCK UV__O_NONBLOCK
#else
# define UV__F_NONBLOCK 1
#endif
int uv__make_socketpair(int fds[2], int flags);
int uv__make_pipe(int fds[2], int flags);
#if defined(__APPLE__)
int uv__fsevents_init(uv_fs_event_t* handle);
int uv__fsevents_close(uv_fs_event_t* handle);
void uv__fsevents_loop_delete(uv_loop_t* loop);
/* OSX < 10.7 has no file events, polyfill them */
#ifndef MAC_OS_X_VERSION_10_7
static const int kFSEventStreamCreateFlagFileEvents = 0x00000010;
static const int kFSEventStreamEventFlagItemCreated = 0x00000100;
static const int kFSEventStreamEventFlagItemRemoved = 0x00000200;
static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400;
static const int kFSEventStreamEventFlagItemRenamed = 0x00000800;
static const int kFSEventStreamEventFlagItemModified = 0x00001000;
static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000;
static const int kFSEventStreamEventFlagItemChangeOwner = 0x00004000;
static const int kFSEventStreamEventFlagItemXattrMod = 0x00008000;
static const int kFSEventStreamEventFlagItemIsFile = 0x00010000;
static const int kFSEventStreamEventFlagItemIsDir = 0x00020000;
static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
#endif /* __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 */
#endif /* defined(__APPLE__) */
UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
/* Use a fast time source if available. We only need millisecond precision.
*/
loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
}
UV_UNUSED(static char* uv__basename_r(const char* path)) {
char* s;
s = strrchr(path, '/');
if (s == NULL)
return (char*) path;
return s + 1;
}
#if defined(__linux__)
int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);
#endif
#endif /* UV_UNIX_INTERNAL_H_ */
@@ -0,0 +1,532 @@
/* 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 "internal.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
/*
* Required on
* - Until at least FreeBSD 11.0
* - Older versions of Mac OS X
*
* http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp
*/
#ifndef EV_OOBAND
#define EV_OOBAND EV_FLAG1
#endif
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
int uv__kqueue_init(uv_loop_t* loop) {
loop->backend_fd = kqueue();
if (loop->backend_fd == -1)
return -errno;
uv__cloexec(loop->backend_fd, 1);
return 0;
}
static int uv__has_forked_with_cfrunloop;
int uv__io_fork(uv_loop_t* loop) {
int err;
uv__close(loop->backend_fd);
loop->backend_fd = -1;
err = uv__kqueue_init(loop);
if (err)
return err;
#if defined(__APPLE__)
if (loop->cf_state != NULL) {
/* We cannot start another CFRunloop and/or thread in the child
process; CF aborts if you try or if you try to touch the thread
at all to kill it. So the best we can do is ignore it from now
on. This means we can't watch directories in the same way
anymore (like other BSDs). It also means we cannot properly
clean up the allocated resources; calling
uv__fsevents_loop_delete from uv_loop_close will crash the
process. So we sidestep the issue by pretending like we never
started it in the first place.
*/
uv__has_forked_with_cfrunloop = 1;
uv__free(loop->cf_state);
loop->cf_state = NULL;
}
#endif
return err;
}
int uv__io_check_fd(uv_loop_t* loop, int fd) {
struct kevent ev;
int rc;
rc = 0;
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
rc = -errno;
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
if (rc == 0)
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
abort();
return rc;
}
void uv__io_poll(uv_loop_t* loop, int timeout) {
struct kevent events[1024];
struct kevent* ev;
struct timespec spec;
unsigned int nevents;
unsigned int revents;
QUEUE* q;
uv__io_t* w;
sigset_t* pset;
sigset_t set;
uint64_t base;
uint64_t diff;
int have_signals;
int filter;
int fflags;
int count;
int nfds;
int fd;
int op;
int i;
if (loop->nfds == 0) {
assert(QUEUE_EMPTY(&loop->watcher_queue));
return;
}
nevents = 0;
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
q = QUEUE_HEAD(&loop->watcher_queue);
QUEUE_REMOVE(q);
QUEUE_INIT(q);
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
assert(w->pevents != 0);
assert(w->fd >= 0);
assert(w->fd < (int) loop->nwatchers);
if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) {
filter = EVFILT_READ;
fflags = 0;
op = EV_ADD;
if (w->cb == uv__fs_event) {
filter = EVFILT_VNODE;
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
}
EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
if (++nevents == ARRAY_SIZE(events)) {
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
abort();
nevents = 0;
}
}
if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
if (++nevents == ARRAY_SIZE(events)) {
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
abort();
nevents = 0;
}
}
if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) {
EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0);
if (++nevents == ARRAY_SIZE(events)) {
if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
abort();
nevents = 0;
}
}
w->events = w->pevents;
}
pset = NULL;
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
pset = &set;
sigemptyset(pset);
sigaddset(pset, SIGPROF);
}
assert(timeout >= -1);
base = loop->time;
count = 48; /* Benchmarks suggest this gives the best throughput. */
for (;; nevents = 0) {
if (timeout != -1) {
spec.tv_sec = timeout / 1000;
spec.tv_nsec = (timeout % 1000) * 1000000;
}
if (pset != NULL)
pthread_sigmask(SIG_BLOCK, pset, NULL);
nfds = kevent(loop->backend_fd,
events,
nevents,
events,
ARRAY_SIZE(events),
timeout == -1 ? NULL : &spec);
if (pset != NULL)
pthread_sigmask(SIG_UNBLOCK, pset, NULL);
/* Update loop->time unconditionally. It's tempting to skip the update when
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
* operating system didn't reschedule our process while in the syscall.
*/
SAVE_ERRNO(uv__update_time(loop));
if (nfds == 0) {
assert(timeout != -1);
return;
}
if (nfds == -1) {
if (errno != EINTR)
abort();
if (timeout == 0)
return;
if (timeout == -1)
continue;
/* Interrupted by a signal. Update timeout and poll again. */
goto update_timeout;
}
have_signals = 0;
nevents = 0;
assert(loop->watchers != NULL);
loop->watchers[loop->nwatchers] = (void*) events;
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
for (i = 0; i < nfds; i++) {
ev = events + i;
fd = ev->ident;
/* Skip invalidated events, see uv__platform_invalidate_fd */
if (fd == -1)
continue;
w = loop->watchers[fd];
if (w == NULL) {
/* File descriptor that we've stopped watching, disarm it. */
/* TODO batch up */
struct kevent events[1];
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
if (errno != EBADF && errno != ENOENT)
abort();
continue;
}
if (ev->filter == EVFILT_VNODE) {
assert(w->events == POLLIN);
assert(w->pevents == POLLIN);
w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
nevents++;
continue;
}
revents = 0;
if (ev->filter == EVFILT_READ) {
if (w->pevents & POLLIN) {
revents |= POLLIN;
w->rcount = ev->data;
} else {
/* TODO batch up */
struct kevent events[1];
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
if (errno != ENOENT)
abort();
}
}
if (ev->filter == EV_OOBAND) {
if (w->pevents & UV__POLLPRI) {
revents |= UV__POLLPRI;
w->rcount = ev->data;
} else {
/* TODO batch up */
struct kevent events[1];
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
if (errno != ENOENT)
abort();
}
}
if (ev->filter == EVFILT_WRITE) {
if (w->pevents & POLLOUT) {
revents |= POLLOUT;
w->wcount = ev->data;
} else {
/* TODO batch up */
struct kevent events[1];
EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
if (errno != ENOENT)
abort();
}
}
if (ev->flags & EV_ERROR)
revents |= POLLERR;
if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
revents |= UV__POLLRDHUP;
if (revents == 0)
continue;
/* Run signal watchers last. This also affects child process watchers
* because those are implemented in terms of signal watchers.
*/
if (w == &loop->signal_io_watcher)
have_signals = 1;
else
w->cb(loop, w, revents);
nevents++;
}
if (have_signals != 0)
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
loop->watchers[loop->nwatchers] = NULL;
loop->watchers[loop->nwatchers + 1] = NULL;
if (have_signals != 0)
return; /* Event loop should cycle now so don't poll again. */
if (nevents != 0) {
if (nfds == ARRAY_SIZE(events) && --count != 0) {
/* Poll for more events but don't block this time. */
timeout = 0;
continue;
}
return;
}
if (timeout == 0)
return;
if (timeout == -1)
continue;
update_timeout:
assert(timeout > 0);
diff = loop->time - base;
if (diff >= (uint64_t) timeout)
return;
timeout -= diff;
}
}
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
struct kevent* events;
uintptr_t i;
uintptr_t nfds;
assert(loop->watchers != NULL);
events = (struct kevent*) loop->watchers[loop->nwatchers];
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
if (events == NULL)
return;
/* Invalidate events with same file descriptor */
for (i = 0; i < nfds; i++)
if ((int) events[i].ident == fd)
events[i].ident = -1;
}
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
uv_fs_event_t* handle;
struct kevent ev;
int events;
const char* path;
#if defined(F_GETPATH)
/* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */
char pathbuf[MAXPATHLEN];
#endif
handle = container_of(w, uv_fs_event_t, event_watcher);
if (fflags & (NOTE_ATTRIB | NOTE_EXTEND))
events = UV_CHANGE;
else
events = UV_RENAME;
path = NULL;
#if defined(F_GETPATH)
/* Also works when the file has been unlinked from the file system. Passing
* in the path when the file has been deleted is arguably a little strange
* but it's consistent with what the inotify backend does.
*/
if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0)
path = uv__basename_r(pathbuf);
#endif
handle->cb(handle, path, events, 0);
if (handle->event_watcher.fd == -1)
return;
/* Watcher operates in one-shot mode, re-arm it. */
fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME
| NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
abort();
}
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* path,
unsigned int flags) {
#if defined(__APPLE__)
struct stat statbuf;
#endif /* defined(__APPLE__) */
int fd;
if (uv__is_active(handle))
return -EINVAL;
/* TODO open asynchronously - but how do we report back errors? */
fd = open(path, O_RDONLY);
if (fd == -1)
return -errno;
uv__handle_start(handle);
uv__io_init(&handle->event_watcher, uv__fs_event, fd);
handle->path = uv__strdup(path);
handle->cb = cb;
#if defined(__APPLE__)
if (uv__has_forked_with_cfrunloop)
goto fallback;
/* Nullify field to perform checks later */
handle->cf_cb = NULL;
handle->realpath = NULL;
handle->realpath_len = 0;
handle->cf_flags = flags;
if (fstat(fd, &statbuf))
goto fallback;
/* FSEvents works only with directories */
if (!(statbuf.st_mode & S_IFDIR))
goto fallback;
/* The fallback fd is no longer needed */
uv__close(fd);
handle->event_watcher.fd = -1;
return uv__fsevents_init(handle);
fallback:
#endif /* defined(__APPLE__) */
uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
return 0;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return 0;
uv__handle_stop(handle);
#if defined(__APPLE__)
if (uv__has_forked_with_cfrunloop || uv__fsevents_close(handle))
#endif /* defined(__APPLE__) */
{
uv__io_close(handle->loop, &handle->event_watcher);
}
uv__free(handle->path);
handle->path = NULL;
if (handle->event_watcher.fd != -1) {
/* When FSEvents is used, we don't use the event_watcher's fd under certain
* confitions. (see uv_fs_event_start) */
uv__close(handle->event_watcher.fd);
handle->event_watcher.fd = -1;
}
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}
@@ -0,0 +1,951 @@
/* 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.
*/
/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
* EPOLL* counterparts. We use the POLL* variants in this file because that
* is what libuv uses elsewhere and it avoids a dependency on <sys/epoll.h>.
*/
#include "uv.h"
#include "internal.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <net/if.h>
#include <sys/param.h>
#include <sys/prctl.h>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#define HAVE_IFADDRS_H 1
#ifdef __UCLIBC__
# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
# undef HAVE_IFADDRS_H
# endif
#endif
#ifdef HAVE_IFADDRS_H
# if defined(__ANDROID__)
# include "android-ifaddrs.h"
# else
# include <ifaddrs.h>
# endif
# include <sys/socket.h>
# include <net/ethernet.h>
# include <netpacket/packet.h>
#endif /* HAVE_IFADDRS_H */
/* Available from 2.6.32 onwards. */
#ifndef CLOCK_MONOTONIC_COARSE
# define CLOCK_MONOTONIC_COARSE 6
#endif
/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
* include that file because it conflicts with <time.h>. We'll just have to
* define it ourselves.
*/
#ifndef CLOCK_BOOTTIME
# define CLOCK_BOOTTIME 7
#endif
static int read_models(unsigned int numcpus, uv_cpu_info_t* ci);
static int read_times(FILE* statfile_fp,
unsigned int numcpus,
uv_cpu_info_t* ci);
static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
static unsigned long read_cpufreq(unsigned int cpunum);
int uv__platform_loop_init(uv_loop_t* loop) {
int fd;
fd = uv__epoll_create1(UV__EPOLL_CLOEXEC);
/* epoll_create1() can fail either because it's not implemented (old kernel)
* or because it doesn't understand the EPOLL_CLOEXEC flag.
*/
if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
fd = uv__epoll_create(256);
if (fd != -1)
uv__cloexec(fd, 1);
}
loop->backend_fd = fd;
loop->inotify_fd = -1;
loop->inotify_watchers = NULL;
if (fd == -1)
return -errno;
return 0;
}
int uv__io_fork(uv_loop_t* loop) {
int err;
void* old_watchers;
old_watchers = loop->inotify_watchers;
uv__close(loop->backend_fd);
loop->backend_fd = -1;
uv__platform_loop_delete(loop);
err = uv__platform_loop_init(loop);
if (err)
return err;
return uv__inotify_fork(loop, old_watchers);
}
void uv__platform_loop_delete(uv_loop_t* loop) {
if (loop->inotify_fd == -1) return;
uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
uv__close(loop->inotify_fd);
loop->inotify_fd = -1;
}
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
struct uv__epoll_event* events;
struct uv__epoll_event dummy;
uintptr_t i;
uintptr_t nfds;
assert(loop->watchers != NULL);
events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers];
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
if (events != NULL)
/* Invalidate events with same file descriptor */
for (i = 0; i < nfds; i++)
if ((int) events[i].data == fd)
events[i].data = -1;
/* Remove the file descriptor from the epoll.
* This avoids a problem where the same file description remains open
* in another process, causing repeated junk epoll events.
*
* We pass in a dummy epoll_event, to work around a bug in old kernels.
*/
if (loop->backend_fd >= 0) {
/* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
* has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
*/
memset(&dummy, 0, sizeof(dummy));
uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &dummy);
}
}
int uv__io_check_fd(uv_loop_t* loop, int fd) {
struct uv__epoll_event e;
int rc;
e.events = POLLIN;
e.data = -1;
rc = 0;
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_ADD, fd, &e))
if (errno != EEXIST)
rc = -errno;
if (rc == 0)
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &e))
abort();
return rc;
}
void uv__io_poll(uv_loop_t* loop, int timeout) {
/* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes
* effectively infinite on 32 bits architectures. To avoid blocking
* indefinitely, we cap the timeout and poll again if necessary.
*
* Note that "30 minutes" is a simplification because it depends on
* the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200,
* that being the largest value I have seen in the wild (and only once.)
*/
static const int max_safe_timeout = 1789569;
static int no_epoll_pwait;
static int no_epoll_wait;
struct uv__epoll_event events[1024];
struct uv__epoll_event* pe;
struct uv__epoll_event e;
int real_timeout;
QUEUE* q;
uv__io_t* w;
sigset_t sigset;
uint64_t sigmask;
uint64_t base;
int have_signals;
int nevents;
int count;
int nfds;
int fd;
int op;
int i;
if (loop->nfds == 0) {
assert(QUEUE_EMPTY(&loop->watcher_queue));
return;
}
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
q = QUEUE_HEAD(&loop->watcher_queue);
QUEUE_REMOVE(q);
QUEUE_INIT(q);
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
assert(w->pevents != 0);
assert(w->fd >= 0);
assert(w->fd < (int) loop->nwatchers);
e.events = w->pevents;
e.data = w->fd;
if (w->events == 0)
op = UV__EPOLL_CTL_ADD;
else
op = UV__EPOLL_CTL_MOD;
/* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
* events, skip the syscall and squelch the events after epoll_wait().
*/
if (uv__epoll_ctl(loop->backend_fd, op, w->fd, &e)) {
if (errno != EEXIST)
abort();
assert(op == UV__EPOLL_CTL_ADD);
/* We've reactivated a file descriptor that's been watched before. */
if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_MOD, w->fd, &e))
abort();
}
w->events = w->pevents;
}
sigmask = 0;
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
sigemptyset(&sigset);
sigaddset(&sigset, SIGPROF);
sigmask |= 1 << (SIGPROF - 1);
}
assert(timeout >= -1);
base = loop->time;
count = 48; /* Benchmarks suggest this gives the best throughput. */
real_timeout = timeout;
for (;;) {
/* See the comment for max_safe_timeout for an explanation of why
* this is necessary. Executive summary: kernel bug workaround.
*/
if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
timeout = max_safe_timeout;
if (sigmask != 0 && no_epoll_pwait != 0)
if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
abort();
if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) {
nfds = uv__epoll_pwait(loop->backend_fd,
events,
ARRAY_SIZE(events),
timeout,
sigmask);
if (nfds == -1 && errno == ENOSYS)
no_epoll_pwait = 1;
} else {
nfds = uv__epoll_wait(loop->backend_fd,
events,
ARRAY_SIZE(events),
timeout);
if (nfds == -1 && errno == ENOSYS)
no_epoll_wait = 1;
}
if (sigmask != 0 && no_epoll_pwait != 0)
if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL))
abort();
/* Update loop->time unconditionally. It's tempting to skip the update when
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
* operating system didn't reschedule our process while in the syscall.
*/
SAVE_ERRNO(uv__update_time(loop));
if (nfds == 0) {
assert(timeout != -1);
if (timeout == 0)
return;
/* We may have been inside the system call for longer than |timeout|
* milliseconds so we need to update the timestamp to avoid drift.
*/
goto update_timeout;
}
if (nfds == -1) {
if (errno == ENOSYS) {
/* epoll_wait() or epoll_pwait() failed, try the other system call. */
assert(no_epoll_wait == 0 || no_epoll_pwait == 0);
continue;
}
if (errno != EINTR)
abort();
if (timeout == -1)
continue;
if (timeout == 0)
return;
/* Interrupted by a signal. Update timeout and poll again. */
goto update_timeout;
}
have_signals = 0;
nevents = 0;
assert(loop->watchers != NULL);
loop->watchers[loop->nwatchers] = (void*) events;
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
for (i = 0; i < nfds; i++) {
pe = events + i;
fd = pe->data;
/* Skip invalidated events, see uv__platform_invalidate_fd */
if (fd == -1)
continue;
assert(fd >= 0);
assert((unsigned) fd < loop->nwatchers);
w = loop->watchers[fd];
if (w == NULL) {
/* File descriptor that we've stopped watching, disarm it.
*
* Ignore all errors because we may be racing with another thread
* when the file descriptor is closed.
*/
uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, pe);
continue;
}
/* Give users only events they're interested in. Prevents spurious
* callbacks when previous callback invocation in this loop has stopped
* the current watcher. Also, filters out events that users has not
* requested us to watch.
*/
pe->events &= w->pevents | POLLERR | POLLHUP;
/* Work around an epoll quirk where it sometimes reports just the
* EPOLLERR or EPOLLHUP event. In order to force the event loop to
* move forward, we merge in the read/write events that the watcher
* is interested in; uv__read() and uv__write() will then deal with
* the error or hangup in the usual fashion.
*
* Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
* reads the available data, calls uv_read_stop(), then sometime later
* calls uv_read_start() again. By then, libuv has forgotten about the
* hangup and the kernel won't report EPOLLIN again because there's
* nothing left to read. If anything, libuv is to blame here. The
* current hack is just a quick bandaid; to properly fix it, libuv
* needs to remember the error/hangup event. We should get that for
* free when we switch over to edge-triggered I/O.
*/
if (pe->events == POLLERR || pe->events == POLLHUP)
pe->events |= w->pevents & (POLLIN | POLLOUT | UV__POLLPRI);
if (pe->events != 0) {
/* Run signal watchers last. This also affects child process watchers
* because those are implemented in terms of signal watchers.
*/
if (w == &loop->signal_io_watcher)
have_signals = 1;
else
w->cb(loop, w, pe->events);
nevents++;
}
}
if (have_signals != 0)
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
loop->watchers[loop->nwatchers] = NULL;
loop->watchers[loop->nwatchers + 1] = NULL;
if (have_signals != 0)
return; /* Event loop should cycle now so don't poll again. */
if (nevents != 0) {
if (nfds == ARRAY_SIZE(events) && --count != 0) {
/* Poll for more events but don't block this time. */
timeout = 0;
continue;
}
return;
}
if (timeout == 0)
return;
if (timeout == -1)
continue;
update_timeout:
assert(timeout > 0);
real_timeout -= (loop->time - base);
if (real_timeout <= 0)
return;
timeout = real_timeout;
}
}
uint64_t uv__hrtime(uv_clocktype_t type) {
static clock_t fast_clock_id = -1;
struct timespec t;
clock_t clock_id;
/* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
* millisecond granularity or better. CLOCK_MONOTONIC_COARSE is
* serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
* decide to make a costly system call.
*/
/* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
* when it has microsecond granularity or better (unlikely).
*/
if (type == UV_CLOCK_FAST && fast_clock_id == -1) {
if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 &&
t.tv_nsec <= 1 * 1000 * 1000) {
fast_clock_id = CLOCK_MONOTONIC_COARSE;
} else {
fast_clock_id = CLOCK_MONOTONIC;
}
}
clock_id = CLOCK_MONOTONIC;
if (type == UV_CLOCK_FAST)
clock_id = fast_clock_id;
if (clock_gettime(clock_id, &t))
return 0; /* Not really possible. */
return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
}
int uv_resident_set_memory(size_t* rss) {
char buf[1024];
const char* s;
ssize_t n;
long val;
int fd;
int i;
do
fd = open("/proc/self/stat", O_RDONLY);
while (fd == -1 && errno == EINTR);
if (fd == -1)
return -errno;
do
n = read(fd, buf, sizeof(buf) - 1);
while (n == -1 && errno == EINTR);
uv__close(fd);
if (n == -1)
return -errno;
buf[n] = '\0';
s = strchr(buf, ' ');
if (s == NULL)
goto err;
s += 1;
if (*s != '(')
goto err;
s = strchr(s, ')');
if (s == NULL)
goto err;
for (i = 1; i <= 22; i++) {
s = strchr(s + 1, ' ');
if (s == NULL)
goto err;
}
errno = 0;
val = strtol(s, NULL, 10);
if (errno != 0)
goto err;
if (val < 0)
goto err;
*rss = val * getpagesize();
return 0;
err:
return -EINVAL;
}
int uv_uptime(double* uptime) {
static volatile int no_clock_boottime;
struct timespec now;
int r;
/* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available
* (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system
* is suspended.
*/
if (no_clock_boottime) {
retry: r = clock_gettime(CLOCK_MONOTONIC, &now);
}
else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) {
no_clock_boottime = 1;
goto retry;
}
if (r)
return -errno;
*uptime = now.tv_sec;
return 0;
}
static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) {
unsigned int num;
char buf[1024];
if (!fgets(buf, sizeof(buf), statfile_fp))
return -EIO;
num = 0;
while (fgets(buf, sizeof(buf), statfile_fp)) {
if (strncmp(buf, "cpu", 3))
break;
num++;
}
if (num == 0)
return -EIO;
*numcpus = num;
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
unsigned int numcpus;
uv_cpu_info_t* ci;
int err;
FILE* statfile_fp;
*cpu_infos = NULL;
*count = 0;
statfile_fp = uv__open_file("/proc/stat");
if (statfile_fp == NULL)
return -errno;
err = uv__cpu_num(statfile_fp, &numcpus);
if (err < 0)
goto out;
err = -ENOMEM;
ci = uv__calloc(numcpus, sizeof(*ci));
if (ci == NULL)
goto out;
err = read_models(numcpus, ci);
if (err == 0)
err = read_times(statfile_fp, numcpus, ci);
if (err) {
uv_free_cpu_info(ci, numcpus);
goto out;
}
/* read_models() on x86 also reads the CPU speed from /proc/cpuinfo.
* We don't check for errors here. Worst case, the field is left zero.
*/
if (ci[0].speed == 0)
read_speeds(numcpus, ci);
*cpu_infos = ci;
*count = numcpus;
err = 0;
out:
if (fclose(statfile_fp))
if (errno != EINTR && errno != EINPROGRESS)
abort();
return err;
}
static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
unsigned int num;
for (num = 0; num < numcpus; num++)
ci[num].speed = read_cpufreq(num) / 1000;
}
/* Also reads the CPU frequency on x86. The other architectures only have
* a BogoMIPS field, which may not be very accurate.
*
* Note: Simply returns on error, uv_cpu_info() takes care of the cleanup.
*/
static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
static const char model_marker[] = "model name\t: ";
static const char speed_marker[] = "cpu MHz\t\t: ";
const char* inferred_model;
unsigned int model_idx;
unsigned int speed_idx;
char buf[1024];
char* model;
FILE* fp;
/* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */
(void) &model_marker;
(void) &speed_marker;
(void) &speed_idx;
(void) &model;
(void) &buf;
(void) &fp;
model_idx = 0;
speed_idx = 0;
#if defined(__arm__) || \
defined(__i386__) || \
defined(__mips__) || \
defined(__x86_64__)
fp = uv__open_file("/proc/cpuinfo");
if (fp == NULL)
return -errno;
while (fgets(buf, sizeof(buf), fp)) {
if (model_idx < numcpus) {
if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
model = buf + sizeof(model_marker) - 1;
model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */
if (model == NULL) {
fclose(fp);
return -ENOMEM;
}
ci[model_idx++].model = model;
continue;
}
}
#if defined(__arm__) || defined(__mips__)
if (model_idx < numcpus) {
#if defined(__arm__)
/* Fallback for pre-3.8 kernels. */
static const char model_marker[] = "Processor\t: ";
#else /* defined(__mips__) */
static const char model_marker[] = "cpu model\t\t: ";
#endif
if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) {
model = buf + sizeof(model_marker) - 1;
model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */
if (model == NULL) {
fclose(fp);
return -ENOMEM;
}
ci[model_idx++].model = model;
continue;
}
}
#else /* !__arm__ && !__mips__ */
if (speed_idx < numcpus) {
if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) {
ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1);
continue;
}
}
#endif /* __arm__ || __mips__ */
}
fclose(fp);
#endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */
/* Now we want to make sure that all the models contain *something* because
* it's not safe to leave them as null. Copy the last entry unless there
* isn't one, in that case we simply put "unknown" into everything.
*/
inferred_model = "unknown";
if (model_idx > 0)
inferred_model = ci[model_idx - 1].model;
while (model_idx < numcpus) {
model = uv__strndup(inferred_model, strlen(inferred_model));
if (model == NULL)
return -ENOMEM;
ci[model_idx++].model = model;
}
return 0;
}
static int read_times(FILE* statfile_fp,
unsigned int numcpus,
uv_cpu_info_t* ci) {
unsigned long clock_ticks;
struct uv_cpu_times_s ts;
unsigned long user;
unsigned long nice;
unsigned long sys;
unsigned long idle;
unsigned long dummy;
unsigned long irq;
unsigned int num;
unsigned int len;
char buf[1024];
clock_ticks = sysconf(_SC_CLK_TCK);
assert(clock_ticks != (unsigned long) -1);
assert(clock_ticks != 0);
rewind(statfile_fp);
if (!fgets(buf, sizeof(buf), statfile_fp))
abort();
num = 0;
while (fgets(buf, sizeof(buf), statfile_fp)) {
if (num >= numcpus)
break;
if (strncmp(buf, "cpu", 3))
break;
/* skip "cpu<num> " marker */
{
unsigned int n;
int r = sscanf(buf, "cpu%u ", &n);
assert(r == 1);
(void) r; /* silence build warning */
for (len = sizeof("cpu0"); n /= 10; len++);
}
/* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
* guest, guest_nice but we're only interested in the first four + irq.
*
* Don't use %*s to skip fields or %ll to read straight into the uint64_t
* fields, they're not allowed in C89 mode.
*/
if (6 != sscanf(buf + len,
"%lu %lu %lu %lu %lu %lu",
&user,
&nice,
&sys,
&idle,
&dummy,
&irq))
abort();
ts.user = clock_ticks * user;
ts.nice = clock_ticks * nice;
ts.sys = clock_ticks * sys;
ts.idle = clock_ticks * idle;
ts.irq = clock_ticks * irq;
ci[num++].cpu_times = ts;
}
assert(num == numcpus);
return 0;
}
static unsigned long read_cpufreq(unsigned int cpunum) {
unsigned long val;
char buf[1024];
FILE* fp;
snprintf(buf,
sizeof(buf),
"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
cpunum);
fp = uv__open_file(buf);
if (fp == NULL)
return 0;
if (fscanf(fp, "%lu", &val) != 1)
val = 0;
fclose(fp);
return val;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(cpu_infos[i].model);
}
uv__free(cpu_infos);
}
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
return 1;
if (ent->ifa_addr == NULL)
return 1;
/*
* On Linux getifaddrs returns information related to the raw underlying
* devices. We're not interested in this information yet.
*/
if (ent->ifa_addr->sa_family == PF_PACKET)
return exclude_type;
return !exclude_type;
}
int uv_interface_addresses(uv_interface_address_t** addresses,
int* count) {
#ifndef HAVE_IFADDRS_H
return -ENOSYS;
#else
struct ifaddrs *addrs, *ent;
uv_interface_address_t* address;
int i;
struct sockaddr_ll *sll;
if (getifaddrs(&addrs))
return -errno;
*count = 0;
*addresses = NULL;
/* Count the number of interfaces */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
continue;
(*count)++;
}
if (*count == 0)
return 0;
*addresses = uv__malloc(*count * sizeof(**addresses));
if (!(*addresses)) {
freeifaddrs(addrs);
return -ENOMEM;
}
address = *addresses;
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
continue;
address->name = uv__strdup(ent->ifa_name);
if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
} else {
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
}
if (ent->ifa_netmask->sa_family == AF_INET6) {
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
} else {
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
}
address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
address++;
}
/* Fill in physical addresses for each interface */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
continue;
address = *addresses;
for (i = 0; i < (*count); i++) {
if (strcmp(address->name, ent->ifa_name) == 0) {
sll = (struct sockaddr_ll*)ent->ifa_addr;
memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
}
address++;
}
}
freeifaddrs(addrs);
return 0;
#endif
}
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(addresses[i].name);
}
uv__free(addresses);
}
void uv__set_process_title(const char* title) {
#if defined(PR_SET_NAME)
prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */
#endif
}
@@ -0,0 +1,352 @@
/* 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 "tree.h"
#include "internal.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
struct watcher_list {
RB_ENTRY(watcher_list) entry;
QUEUE watchers;
int iterating;
char* path;
int wd;
};
struct watcher_root {
struct watcher_list* rbh_root;
};
#define CAST(p) ((struct watcher_root*)(p))
static int compare_watchers(const struct watcher_list* a,
const struct watcher_list* b) {
if (a->wd < b->wd) return -1;
if (a->wd > b->wd) return 1;
return 0;
}
RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers)
static void uv__inotify_read(uv_loop_t* loop,
uv__io_t* w,
unsigned int revents);
static void maybe_free_watcher_list(struct watcher_list* w,
uv_loop_t* loop);
static int new_inotify_fd(void) {
int err;
int fd;
fd = uv__inotify_init1(UV__IN_NONBLOCK | UV__IN_CLOEXEC);
if (fd != -1)
return fd;
if (errno != ENOSYS)
return -errno;
fd = uv__inotify_init();
if (fd == -1)
return -errno;
err = uv__cloexec(fd, 1);
if (err == 0)
err = uv__nonblock(fd, 1);
if (err) {
uv__close(fd);
return err;
}
return fd;
}
static int init_inotify(uv_loop_t* loop) {
int err;
if (loop->inotify_fd != -1)
return 0;
err = new_inotify_fd();
if (err < 0)
return err;
loop->inotify_fd = err;
uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);
uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);
return 0;
}
int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) {
/* Open the inotify_fd, and re-arm all the inotify watchers. */
int err;
struct watcher_list* tmp_watcher_list_iter;
struct watcher_list* watcher_list;
struct watcher_list tmp_watcher_list;
QUEUE queue;
QUEUE* q;
uv_fs_event_t* handle;
char* tmp_path;
if (old_watchers != NULL) {
/* We must restore the old watcher list to be able to close items
* out of it.
*/
loop->inotify_watchers = old_watchers;
QUEUE_INIT(&tmp_watcher_list.watchers);
/* Note that the queue we use is shared with the start and stop()
* functions, making QUEUE_FOREACH unsafe to use. So we use the
* QUEUE_MOVE trick to safely iterate. Also don't free the watcher
* list until we're done iterating. c.f. uv__inotify_read.
*/
RB_FOREACH_SAFE(watcher_list, watcher_root,
CAST(&old_watchers), tmp_watcher_list_iter) {
watcher_list->iterating = 1;
QUEUE_MOVE(&watcher_list->watchers, &queue);
while (!QUEUE_EMPTY(&queue)) {
q = QUEUE_HEAD(&queue);
handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
/* It's critical to keep a copy of path here, because it
* will be set to NULL by stop() and then deallocated by
* maybe_free_watcher_list
*/
tmp_path = uv__strdup(handle->path);
assert(tmp_path != NULL);
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&watcher_list->watchers, q);
uv_fs_event_stop(handle);
QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers);
handle->path = tmp_path;
}
watcher_list->iterating = 0;
maybe_free_watcher_list(watcher_list, loop);
}
QUEUE_MOVE(&tmp_watcher_list.watchers, &queue);
while (!QUEUE_EMPTY(&queue)) {
q = QUEUE_HEAD(&queue);
QUEUE_REMOVE(q);
handle = QUEUE_DATA(q, uv_fs_event_t, watchers);
tmp_path = handle->path;
handle->path = NULL;
err = uv_fs_event_start(handle, handle->cb, tmp_path, 0);
uv__free(tmp_path);
if (err)
return err;
}
}
return 0;
}
static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) {
struct watcher_list w;
w.wd = wd;
return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w);
}
static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
/* if the watcher_list->watchers is being iterated over, we can't free it. */
if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) {
/* No watchers left for this path. Clean up. */
RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w);
uv__inotify_rm_watch(loop->inotify_fd, w->wd);
uv__free(w);
}
}
static void uv__inotify_read(uv_loop_t* loop,
uv__io_t* dummy,
unsigned int events) {
const struct uv__inotify_event* e;
struct watcher_list* w;
uv_fs_event_t* h;
QUEUE queue;
QUEUE* q;
const char* path;
ssize_t size;
const char *p;
/* needs to be large enough for sizeof(inotify_event) + strlen(path) */
char buf[4096];
while (1) {
do
size = read(loop->inotify_fd, buf, sizeof(buf));
while (size == -1 && errno == EINTR);
if (size == -1) {
assert(errno == EAGAIN || errno == EWOULDBLOCK);
break;
}
assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */
/* Now we have one or more inotify_event structs. */
for (p = buf; p < buf + size; p += sizeof(*e) + e->len) {
e = (const struct uv__inotify_event*)p;
events = 0;
if (e->mask & (UV__IN_ATTRIB|UV__IN_MODIFY))
events |= UV_CHANGE;
if (e->mask & ~(UV__IN_ATTRIB|UV__IN_MODIFY))
events |= UV_RENAME;
w = find_watcher(loop, e->wd);
if (w == NULL)
continue; /* Stale event, no watchers left. */
/* inotify does not return the filename when monitoring a single file
* for modifications. Repurpose the filename for API compatibility.
* I'm not convinced this is a good thing, maybe it should go.
*/
path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
/* We're about to iterate over the queue and call user's callbacks.
* What can go wrong?
* A callback could call uv_fs_event_stop()
* and the queue can change under our feet.
* So, we use QUEUE_MOVE() trick to safely iterate over the queue.
* And we don't free the watcher_list until we're done iterating.
*
* First,
* tell uv_fs_event_stop() (that could be called from a user's callback)
* not to free watcher_list.
*/
w->iterating = 1;
QUEUE_MOVE(&w->watchers, &queue);
while (!QUEUE_EMPTY(&queue)) {
q = QUEUE_HEAD(&queue);
h = QUEUE_DATA(q, uv_fs_event_t, watchers);
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&w->watchers, q);
h->cb(h, path, events, 0);
}
/* done iterating, time to (maybe) free empty watcher_list */
w->iterating = 0;
maybe_free_watcher_list(w, loop);
}
}
}
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* path,
unsigned int flags) {
struct watcher_list* w;
int events;
int err;
int wd;
if (uv__is_active(handle))
return -EINVAL;
err = init_inotify(handle->loop);
if (err)
return err;
events = UV__IN_ATTRIB
| UV__IN_CREATE
| UV__IN_MODIFY
| UV__IN_DELETE
| UV__IN_DELETE_SELF
| UV__IN_MOVE_SELF
| UV__IN_MOVED_FROM
| UV__IN_MOVED_TO;
wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events);
if (wd == -1)
return -errno;
w = find_watcher(handle->loop, wd);
if (w)
goto no_insert;
w = uv__malloc(sizeof(*w) + strlen(path) + 1);
if (w == NULL)
return -ENOMEM;
w->wd = wd;
w->path = strcpy((char*)(w + 1), path);
QUEUE_INIT(&w->watchers);
w->iterating = 0;
RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
no_insert:
uv__handle_start(handle);
QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
handle->path = w->path;
handle->cb = cb;
handle->wd = wd;
return 0;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
struct watcher_list* w;
if (!uv__is_active(handle))
return 0;
w = find_watcher(handle->loop, handle->wd);
assert(w != NULL);
handle->wd = -1;
handle->path = NULL;
uv__handle_stop(handle);
QUEUE_REMOVE(&handle->watchers);
maybe_free_watcher_list(w, handle->loop);
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}
@@ -0,0 +1,471 @@
/* 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 "linux-syscalls.h"
#include <unistd.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <errno.h>
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# define MSAN_ACTIVE 1
# include <sanitizer/msan_interface.h>
# endif
#endif
#if defined(__i386__)
# ifndef __NR_socketcall
# define __NR_socketcall 102
# endif
#endif
#if defined(__arm__)
# if defined(__thumb__) || defined(__ARM_EABI__)
# define UV_SYSCALL_BASE 0
# else
# define UV_SYSCALL_BASE 0x900000
# endif
#endif /* __arm__ */
#ifndef __NR_accept4
# if defined(__x86_64__)
# define __NR_accept4 288
# elif defined(__i386__)
/* Nothing. Handled through socketcall(). */
# elif defined(__arm__)
# define __NR_accept4 (UV_SYSCALL_BASE + 366)
# endif
#endif /* __NR_accept4 */
#ifndef __NR_eventfd
# if defined(__x86_64__)
# define __NR_eventfd 284
# elif defined(__i386__)
# define __NR_eventfd 323
# elif defined(__arm__)
# define __NR_eventfd (UV_SYSCALL_BASE + 351)
# endif
#endif /* __NR_eventfd */
#ifndef __NR_eventfd2
# if defined(__x86_64__)
# define __NR_eventfd2 290
# elif defined(__i386__)
# define __NR_eventfd2 328
# elif defined(__arm__)
# define __NR_eventfd2 (UV_SYSCALL_BASE + 356)
# endif
#endif /* __NR_eventfd2 */
#ifndef __NR_epoll_create
# if defined(__x86_64__)
# define __NR_epoll_create 213
# elif defined(__i386__)
# define __NR_epoll_create 254
# elif defined(__arm__)
# define __NR_epoll_create (UV_SYSCALL_BASE + 250)
# endif
#endif /* __NR_epoll_create */
#ifndef __NR_epoll_create1
# if defined(__x86_64__)
# define __NR_epoll_create1 291
# elif defined(__i386__)
# define __NR_epoll_create1 329
# elif defined(__arm__)
# define __NR_epoll_create1 (UV_SYSCALL_BASE + 357)
# endif
#endif /* __NR_epoll_create1 */
#ifndef __NR_epoll_ctl
# if defined(__x86_64__)
# define __NR_epoll_ctl 233 /* used to be 214 */
# elif defined(__i386__)
# define __NR_epoll_ctl 255
# elif defined(__arm__)
# define __NR_epoll_ctl (UV_SYSCALL_BASE + 251)
# endif
#endif /* __NR_epoll_ctl */
#ifndef __NR_epoll_wait
# if defined(__x86_64__)
# define __NR_epoll_wait 232 /* used to be 215 */
# elif defined(__i386__)
# define __NR_epoll_wait 256
# elif defined(__arm__)
# define __NR_epoll_wait (UV_SYSCALL_BASE + 252)
# endif
#endif /* __NR_epoll_wait */
#ifndef __NR_epoll_pwait
# if defined(__x86_64__)
# define __NR_epoll_pwait 281
# elif defined(__i386__)
# define __NR_epoll_pwait 319
# elif defined(__arm__)
# define __NR_epoll_pwait (UV_SYSCALL_BASE + 346)
# endif
#endif /* __NR_epoll_pwait */
#ifndef __NR_inotify_init
# if defined(__x86_64__)
# define __NR_inotify_init 253
# elif defined(__i386__)
# define __NR_inotify_init 291
# elif defined(__arm__)
# define __NR_inotify_init (UV_SYSCALL_BASE + 316)
# endif
#endif /* __NR_inotify_init */
#ifndef __NR_inotify_init1
# if defined(__x86_64__)
# define __NR_inotify_init1 294
# elif defined(__i386__)
# define __NR_inotify_init1 332
# elif defined(__arm__)
# define __NR_inotify_init1 (UV_SYSCALL_BASE + 360)
# endif
#endif /* __NR_inotify_init1 */
#ifndef __NR_inotify_add_watch
# if defined(__x86_64__)
# define __NR_inotify_add_watch 254
# elif defined(__i386__)
# define __NR_inotify_add_watch 292
# elif defined(__arm__)
# define __NR_inotify_add_watch (UV_SYSCALL_BASE + 317)
# endif
#endif /* __NR_inotify_add_watch */
#ifndef __NR_inotify_rm_watch
# if defined(__x86_64__)
# define __NR_inotify_rm_watch 255
# elif defined(__i386__)
# define __NR_inotify_rm_watch 293
# elif defined(__arm__)
# define __NR_inotify_rm_watch (UV_SYSCALL_BASE + 318)
# endif
#endif /* __NR_inotify_rm_watch */
#ifndef __NR_pipe2
# if defined(__x86_64__)
# define __NR_pipe2 293
# elif defined(__i386__)
# define __NR_pipe2 331
# elif defined(__arm__)
# define __NR_pipe2 (UV_SYSCALL_BASE + 359)
# endif
#endif /* __NR_pipe2 */
#ifndef __NR_recvmmsg
# if defined(__x86_64__)
# define __NR_recvmmsg 299
# elif defined(__i386__)
# define __NR_recvmmsg 337
# elif defined(__arm__)
# define __NR_recvmmsg (UV_SYSCALL_BASE + 365)
# endif
#endif /* __NR_recvmsg */
#ifndef __NR_sendmmsg
# if defined(__x86_64__)
# define __NR_sendmmsg 307
# elif defined(__i386__)
# define __NR_sendmmsg 345
# elif defined(__arm__)
# define __NR_sendmmsg (UV_SYSCALL_BASE + 374)
# endif
#endif /* __NR_sendmmsg */
#ifndef __NR_utimensat
# if defined(__x86_64__)
# define __NR_utimensat 280
# elif defined(__i386__)
# define __NR_utimensat 320
# elif defined(__arm__)
# define __NR_utimensat (UV_SYSCALL_BASE + 348)
# endif
#endif /* __NR_utimensat */
#ifndef __NR_preadv
# if defined(__x86_64__)
# define __NR_preadv 295
# elif defined(__i386__)
# define __NR_preadv 333
# elif defined(__arm__)
# define __NR_preadv (UV_SYSCALL_BASE + 361)
# endif
#endif /* __NR_preadv */
#ifndef __NR_pwritev
# if defined(__x86_64__)
# define __NR_pwritev 296
# elif defined(__i386__)
# define __NR_pwritev 334
# elif defined(__arm__)
# define __NR_pwritev (UV_SYSCALL_BASE + 362)
# endif
#endif /* __NR_pwritev */
#ifndef __NR_dup3
# if defined(__x86_64__)
# define __NR_dup3 292
# elif defined(__i386__)
# define __NR_dup3 330
# elif defined(__arm__)
# define __NR_dup3 (UV_SYSCALL_BASE + 358)
# endif
#endif /* __NR_pwritev */
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
#if defined(__i386__)
unsigned long args[4];
int r;
args[0] = (unsigned long) fd;
args[1] = (unsigned long) addr;
args[2] = (unsigned long) addrlen;
args[3] = (unsigned long) flags;
r = syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args);
/* socketcall() raises EINVAL when SYS_ACCEPT4 is not supported but so does
* a bad flags argument. Try to distinguish between the two cases.
*/
if (r == -1)
if (errno == EINVAL)
if ((flags & ~(UV__SOCK_CLOEXEC|UV__SOCK_NONBLOCK)) == 0)
errno = ENOSYS;
return r;
#elif defined(__NR_accept4)
return syscall(__NR_accept4, fd, addr, addrlen, flags);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__eventfd(unsigned int count) {
#if defined(__NR_eventfd)
return syscall(__NR_eventfd, count);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__eventfd2(unsigned int count, int flags) {
#if defined(__NR_eventfd2)
return syscall(__NR_eventfd2, count, flags);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__epoll_create(int size) {
#if defined(__NR_epoll_create)
return syscall(__NR_epoll_create, size);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__epoll_create1(int flags) {
#if defined(__NR_epoll_create1)
return syscall(__NR_epoll_create1, flags);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event* events) {
#if defined(__NR_epoll_ctl)
return syscall(__NR_epoll_ctl, epfd, op, fd, events);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__epoll_wait(int epfd,
struct uv__epoll_event* events,
int nevents,
int timeout) {
#if defined(__NR_epoll_wait)
int result;
result = syscall(__NR_epoll_wait, epfd, events, nevents, timeout);
#if MSAN_ACTIVE
if (result > 0)
__msan_unpoison(events, sizeof(events[0]) * result);
#endif
return result;
#else
return errno = ENOSYS, -1;
#endif
}
int uv__epoll_pwait(int epfd,
struct uv__epoll_event* events,
int nevents,
int timeout,
uint64_t sigmask) {
#if defined(__NR_epoll_pwait)
int result;
result = syscall(__NR_epoll_pwait,
epfd,
events,
nevents,
timeout,
&sigmask,
sizeof(sigmask));
#if MSAN_ACTIVE
if (result > 0)
__msan_unpoison(events, sizeof(events[0]) * result);
#endif
return result;
#else
return errno = ENOSYS, -1;
#endif
}
int uv__inotify_init(void) {
#if defined(__NR_inotify_init)
return syscall(__NR_inotify_init);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__inotify_init1(int flags) {
#if defined(__NR_inotify_init1)
return syscall(__NR_inotify_init1, flags);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__inotify_add_watch(int fd, const char* path, uint32_t mask) {
#if defined(__NR_inotify_add_watch)
return syscall(__NR_inotify_add_watch, fd, path, mask);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__inotify_rm_watch(int fd, int32_t wd) {
#if defined(__NR_inotify_rm_watch)
return syscall(__NR_inotify_rm_watch, fd, wd);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__pipe2(int pipefd[2], int flags) {
#if defined(__NR_pipe2)
int result;
result = syscall(__NR_pipe2, pipefd, flags);
#if MSAN_ACTIVE
if (!result)
__msan_unpoison(pipefd, sizeof(int[2]));
#endif
return result;
#else
return errno = ENOSYS, -1;
#endif
}
int uv__sendmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags) {
#if defined(__NR_sendmmsg)
return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__recvmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags,
struct timespec* timeout) {
#if defined(__NR_recvmmsg)
return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout);
#else
return errno = ENOSYS, -1;
#endif
}
int uv__utimesat(int dirfd,
const char* path,
const struct timespec times[2],
int flags)
{
#if defined(__NR_utimensat)
return syscall(__NR_utimensat, dirfd, path, times, flags);
#else
return errno = ENOSYS, -1;
#endif
}
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
#if defined(__NR_preadv)
return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
#else
return errno = ENOSYS, -1;
#endif
}
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
#if defined(__NR_pwritev)
return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
#else
return errno = ENOSYS, -1;
#endif
}
int uv__dup3(int oldfd, int newfd, int flags) {
#if defined(__NR_dup3)
return syscall(__NR_dup3, oldfd, newfd, flags);
#else
return errno = ENOSYS, -1;
#endif
}
@@ -0,0 +1,151 @@
/* 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.
*/
#ifndef UV_LINUX_SYSCALL_H_
#define UV_LINUX_SYSCALL_H_
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <stdint.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#if defined(__alpha__)
# define UV__O_CLOEXEC 0x200000
#elif defined(__hppa__)
# define UV__O_CLOEXEC 0x200000
#elif defined(__sparc__)
# define UV__O_CLOEXEC 0x400000
#else
# define UV__O_CLOEXEC 0x80000
#endif
#if defined(__alpha__)
# define UV__O_NONBLOCK 0x4
#elif defined(__hppa__)
# define UV__O_NONBLOCK O_NONBLOCK
#elif defined(__mips__)
# define UV__O_NONBLOCK 0x80
#elif defined(__sparc__)
# define UV__O_NONBLOCK 0x4000
#else
# define UV__O_NONBLOCK 0x800
#endif
#define UV__EFD_CLOEXEC UV__O_CLOEXEC
#define UV__EFD_NONBLOCK UV__O_NONBLOCK
#define UV__IN_CLOEXEC UV__O_CLOEXEC
#define UV__IN_NONBLOCK UV__O_NONBLOCK
#define UV__SOCK_CLOEXEC UV__O_CLOEXEC
#if defined(SOCK_NONBLOCK)
# define UV__SOCK_NONBLOCK SOCK_NONBLOCK
#else
# define UV__SOCK_NONBLOCK UV__O_NONBLOCK
#endif
/* epoll flags */
#define UV__EPOLL_CLOEXEC UV__O_CLOEXEC
#define UV__EPOLL_CTL_ADD 1
#define UV__EPOLL_CTL_DEL 2
#define UV__EPOLL_CTL_MOD 3
/* inotify flags */
#define UV__IN_ACCESS 0x001
#define UV__IN_MODIFY 0x002
#define UV__IN_ATTRIB 0x004
#define UV__IN_CLOSE_WRITE 0x008
#define UV__IN_CLOSE_NOWRITE 0x010
#define UV__IN_OPEN 0x020
#define UV__IN_MOVED_FROM 0x040
#define UV__IN_MOVED_TO 0x080
#define UV__IN_CREATE 0x100
#define UV__IN_DELETE 0x200
#define UV__IN_DELETE_SELF 0x400
#define UV__IN_MOVE_SELF 0x800
#if defined(__x86_64__)
struct uv__epoll_event {
uint32_t events;
uint64_t data;
} __attribute__((packed));
#else
struct uv__epoll_event {
uint32_t events;
uint64_t data;
};
#endif
struct uv__inotify_event {
int32_t wd;
uint32_t mask;
uint32_t cookie;
uint32_t len;
/* char name[0]; */
};
struct uv__mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags);
int uv__eventfd(unsigned int count);
int uv__epoll_create(int size);
int uv__epoll_create1(int flags);
int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event *ev);
int uv__epoll_wait(int epfd,
struct uv__epoll_event* events,
int nevents,
int timeout);
int uv__epoll_pwait(int epfd,
struct uv__epoll_event* events,
int nevents,
int timeout,
uint64_t sigmask);
int uv__eventfd2(unsigned int count, int flags);
int uv__inotify_init(void);
int uv__inotify_init1(int flags);
int uv__inotify_add_watch(int fd, const char* path, uint32_t mask);
int uv__inotify_rm_watch(int fd, int32_t wd);
int uv__pipe2(int pipefd[2], int flags);
int uv__recvmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags,
struct timespec* timeout);
int uv__sendmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags);
int uv__utimesat(int dirfd,
const char* path,
const struct timespec times[2],
int flags);
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
int uv__dup3(int oldfd, int newfd, int flags);
#endif /* UV_LINUX_SYSCALL_H_ */
@@ -0,0 +1,68 @@
/* 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 "internal.h"
#define UV_LOOP_WATCHER_DEFINE(name, type) \
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \
handle->name##_cb = NULL; \
return 0; \
} \
\
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
if (uv__is_active(handle)) return 0; \
if (cb == NULL) return -EINVAL; \
QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \
handle->name##_cb = cb; \
uv__handle_start(handle); \
return 0; \
} \
\
int uv_##name##_stop(uv_##name##_t* handle) { \
if (!uv__is_active(handle)) return 0; \
QUEUE_REMOVE(&handle->queue); \
uv__handle_stop(handle); \
return 0; \
} \
\
void uv__run_##name(uv_loop_t* loop) { \
uv_##name##_t* h; \
QUEUE queue; \
QUEUE* q; \
QUEUE_MOVE(&loop->name##_handles, &queue); \
while (!QUEUE_EMPTY(&queue)) { \
q = QUEUE_HEAD(&queue); \
h = QUEUE_DATA(q, uv_##name##_t, queue); \
QUEUE_REMOVE(q); \
QUEUE_INSERT_TAIL(&loop->name##_handles, q); \
h->name##_cb(h); \
} \
} \
\
void uv__##name##_close(uv_##name##_t* handle) { \
uv_##name##_stop(handle); \
}
UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
UV_LOOP_WATCHER_DEFINE(check, CHECK)
UV_LOOP_WATCHER_DEFINE(idle, IDLE)
@@ -0,0 +1,193 @@
/* 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 "tree.h"
#include "internal.h"
#include "heap-inl.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int uv_loop_init(uv_loop_t* loop) {
void* saved_data;
int err;
uv__signal_global_once_init();
saved_data = loop->data;
memset(loop, 0, sizeof(*loop));
loop->data = saved_data;
heap_init((struct heap*) &loop->timer_heap);
QUEUE_INIT(&loop->wq);
QUEUE_INIT(&loop->active_reqs);
QUEUE_INIT(&loop->idle_handles);
QUEUE_INIT(&loop->async_handles);
QUEUE_INIT(&loop->check_handles);
QUEUE_INIT(&loop->prepare_handles);
QUEUE_INIT(&loop->handle_queue);
loop->nfds = 0;
loop->watchers = NULL;
loop->nwatchers = 0;
QUEUE_INIT(&loop->pending_queue);
QUEUE_INIT(&loop->watcher_queue);
loop->closing_handles = NULL;
uv__update_time(loop);
loop->async_io_watcher.fd = -1;
loop->async_wfd = -1;
loop->signal_pipefd[0] = -1;
loop->signal_pipefd[1] = -1;
loop->backend_fd = -1;
loop->emfile_fd = -1;
loop->timer_counter = 0;
loop->stop_flag = 0;
err = uv__platform_loop_init(loop);
if (err)
return err;
err = uv_signal_init(loop, &loop->child_watcher);
if (err)
goto fail_signal_init;
uv__handle_unref(&loop->child_watcher);
loop->child_watcher.flags |= UV__HANDLE_INTERNAL;
QUEUE_INIT(&loop->process_handles);
err = uv_rwlock_init(&loop->cloexec_lock);
if (err)
goto fail_rwlock_init;
err = uv_mutex_init(&loop->wq_mutex);
if (err)
goto fail_mutex_init;
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
if (err)
goto fail_async_init;
uv__handle_unref(&loop->wq_async);
loop->wq_async.flags |= UV__HANDLE_INTERNAL;
return 0;
fail_async_init:
uv_mutex_destroy(&loop->wq_mutex);
fail_mutex_init:
uv_rwlock_destroy(&loop->cloexec_lock);
fail_rwlock_init:
uv__signal_loop_cleanup(loop);
fail_signal_init:
uv__platform_loop_delete(loop);
return err;
}
int uv_loop_fork(uv_loop_t* loop) {
int err;
unsigned int i;
uv__io_t* w;
err = uv__io_fork(loop);
if (err)
return err;
err = uv__async_fork(loop);
if (err)
return err;
err = uv__signal_loop_fork(loop);
if (err)
return err;
/* Rearm all the watchers that aren't re-queued by the above. */
for (i = 0; i < loop->nwatchers; i++) {
w = loop->watchers[i];
if (w == NULL)
continue;
if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) {
w->events = 0; /* Force re-registration in uv__io_poll. */
QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
}
}
return 0;
}
void uv__loop_close(uv_loop_t* loop) {
uv__signal_loop_cleanup(loop);
uv__platform_loop_delete(loop);
uv__async_stop(loop);
if (loop->emfile_fd != -1) {
uv__close(loop->emfile_fd);
loop->emfile_fd = -1;
}
if (loop->backend_fd != -1) {
uv__close(loop->backend_fd);
loop->backend_fd = -1;
}
uv_mutex_lock(&loop->wq_mutex);
assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
assert(!uv__has_active_reqs(loop));
uv_mutex_unlock(&loop->wq_mutex);
uv_mutex_destroy(&loop->wq_mutex);
/*
* Note that all thread pool stuff is finished at this point and
* it is safe to just destroy rw lock
*/
uv_rwlock_destroy(&loop->cloexec_lock);
#if 0
assert(QUEUE_EMPTY(&loop->pending_queue));
assert(QUEUE_EMPTY(&loop->watcher_queue));
assert(loop->nfds == 0);
#endif
uv__free(loop->watchers);
loop->watchers = NULL;
loop->nwatchers = 0;
}
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
if (option != UV_LOOP_BLOCK_SIGNAL)
return UV_ENOSYS;
if (va_arg(ap, int) != SIGPROF)
return UV_EINVAL;
loop->flags |= UV_LOOP_BLOCK_SIGPROF;
return 0;
}
@@ -0,0 +1,272 @@
/* 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 "internal.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <kvm.h>
#include <paths.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <uvm/uvm_extern.h>
#include <unistd.h>
#include <time.h>
static char *process_title;
int uv__platform_loop_init(uv_loop_t* loop) {
return uv__kqueue_init(loop);
}
void uv__platform_loop_delete(uv_loop_t* loop) {
}
void uv_loadavg(double avg[3]) {
struct loadavg info;
size_t size = sizeof(info);
int which[] = {CTL_VM, VM_LOADAVG};
if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return;
avg[0] = (double) info.ldavg[0] / info.fscale;
avg[1] = (double) info.ldavg[1] / info.fscale;
avg[2] = (double) info.ldavg[2] / info.fscale;
}
int uv_exepath(char* buffer, size_t* size) {
int mib[4];
size_t cb;
pid_t mypid;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
mypid = getpid();
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = mypid;
mib[3] = KERN_PROC_ARGV;
cb = *size;
if (sysctl(mib, 4, buffer, &cb, NULL, 0))
return -errno;
*size = strlen(buffer);
return 0;
}
uint64_t uv_get_free_memory(void) {
struct uvmexp info;
size_t size = sizeof(info);
int which[] = {CTL_VM, VM_UVMEXP};
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
}
uint64_t uv_get_total_memory(void) {
#if defined(HW_PHYSMEM64)
uint64_t info;
int which[] = {CTL_HW, HW_PHYSMEM64};
#else
unsigned int info;
int which[] = {CTL_HW, HW_PHYSMEM};
#endif
size_t size = sizeof(info);
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
return (uint64_t) info;
}
char** uv_setup_args(int argc, char** argv) {
process_title = argc ? uv__strdup(argv[0]) : NULL;
return argv;
}
int uv_set_process_title(const char* title) {
if (process_title) uv__free(process_title);
process_title = uv__strdup(title);
setproctitle("%s", title);
return 0;
}
int uv_get_process_title(char* buffer, size_t size) {
size_t len;
if (buffer == NULL || size == 0)
return -EINVAL;
if (process_title) {
len = strlen(process_title) + 1;
if (size < len)
return -ENOBUFS;
memcpy(buffer, process_title, len);
} else {
len = 0;
}
buffer[len] = '\0';
return 0;
}
int uv_resident_set_memory(size_t* rss) {
kvm_t *kd = NULL;
struct kinfo_proc2 *kinfo = NULL;
pid_t pid;
int nprocs;
int max_size = sizeof(struct kinfo_proc2);
int page_size;
page_size = getpagesize();
pid = getpid();
kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
if (kd == NULL) goto error;
kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs);
if (kinfo == NULL) goto error;
*rss = kinfo->p_vm_rssize * page_size;
kvm_close(kd);
return 0;
error:
if (kd) kvm_close(kd);
return -EPERM;
}
int uv_uptime(double* uptime) {
time_t now;
struct timeval info;
size_t size = sizeof(info);
static int which[] = {CTL_KERN, KERN_BOOTTIME};
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
now = time(NULL);
*uptime = (double)(now - info.tv_sec);
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK);
unsigned int multiplier = ((uint64_t)1000L / ticks);
unsigned int cur = 0;
uv_cpu_info_t* cpu_info;
u_int64_t* cp_times;
char model[512];
u_int64_t cpuspeed;
int numcpus;
size_t size;
int i;
size = sizeof(model);
if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) &&
sysctlbyname("hw.model", &model, &size, NULL, 0)) {
return -errno;
}
size = sizeof(numcpus);
if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
return -errno;
*count = numcpus;
/* Only i386 and amd64 have machdep.tsc_freq */
size = sizeof(cpuspeed);
if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0))
cpuspeed = 0;
size = numcpus * CPUSTATES * sizeof(*cp_times);
cp_times = uv__malloc(size);
if (cp_times == NULL)
return -ENOMEM;
if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0))
return -errno;
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
if (!(*cpu_infos)) {
uv__free(cp_times);
uv__free(*cpu_infos);
return -ENOMEM;
}
for (i = 0; i < numcpus; i++) {
cpu_info = &(*cpu_infos)[i];
cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
cpu_info->model = uv__strdup(model);
cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6);
cur += CPUSTATES;
}
uv__free(cp_times);
return 0;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(cpu_infos[i].model);
}
uv__free(cpu_infos);
}
@@ -0,0 +1,42 @@
/* Copyright libuv project 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 "internal.h"
#include <errno.h>
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
return -ENOSYS;
}
int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
const char* filename, unsigned int flags) {
return -ENOSYS;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
return -ENOSYS;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
UNREACHABLE();
}
@@ -0,0 +1,42 @@
/* Copyright libuv project 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 "internal.h"
#include <errno.h>
#include <stddef.h>
char** uv_setup_args(int argc, char** argv) {
return argv;
}
int uv_set_process_title(const char* title) {
return 0;
}
int uv_get_process_title(char* buffer, size_t size) {
if (buffer == NULL || size == 0)
return -EINVAL;
buffer[0] = '\0';
return 0;
}
@@ -0,0 +1,284 @@
/* 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 "internal.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/sched.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char *process_title;
int uv__platform_loop_init(uv_loop_t* loop) {
return uv__kqueue_init(loop);
}
void uv__platform_loop_delete(uv_loop_t* loop) {
}
void uv_loadavg(double avg[3]) {
struct loadavg info;
size_t size = sizeof(info);
int which[] = {CTL_VM, VM_LOADAVG};
if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
avg[0] = (double) info.ldavg[0] / info.fscale;
avg[1] = (double) info.ldavg[1] / info.fscale;
avg[2] = (double) info.ldavg[2] / info.fscale;
}
int uv_exepath(char* buffer, size_t* size) {
int mib[4];
char **argsbuf = NULL;
char **argsbuf_tmp;
size_t argsbuf_size = 100U;
size_t exepath_size;
pid_t mypid;
int err;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
mypid = getpid();
for (;;) {
err = -ENOMEM;
argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size);
if (argsbuf_tmp == NULL)
goto out;
argsbuf = argsbuf_tmp;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = mypid;
mib[3] = KERN_PROC_ARGV;
if (sysctl(mib, 4, argsbuf, &argsbuf_size, NULL, 0) == 0) {
break;
}
if (errno != ENOMEM) {
err = -errno;
goto out;
}
argsbuf_size *= 2U;
}
if (argsbuf[0] == NULL) {
err = -EINVAL; /* FIXME(bnoordhuis) More appropriate error. */
goto out;
}
*size -= 1;
exepath_size = strlen(argsbuf[0]);
if (*size > exepath_size)
*size = exepath_size;
memcpy(buffer, argsbuf[0], *size);
buffer[*size] = '\0';
err = 0;
out:
uv__free(argsbuf);
return err;
}
uint64_t uv_get_free_memory(void) {
struct uvmexp info;
size_t size = sizeof(info);
int which[] = {CTL_VM, VM_UVMEXP};
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
}
uint64_t uv_get_total_memory(void) {
uint64_t info;
int which[] = {CTL_HW, HW_PHYSMEM64};
size_t size = sizeof(info);
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
return (uint64_t) info;
}
char** uv_setup_args(int argc, char** argv) {
process_title = argc ? uv__strdup(argv[0]) : NULL;
return argv;
}
int uv_set_process_title(const char* title) {
uv__free(process_title);
process_title = uv__strdup(title);
setproctitle("%s", title);
return 0;
}
int uv_get_process_title(char* buffer, size_t size) {
size_t len;
if (buffer == NULL || size == 0)
return -EINVAL;
if (process_title) {
len = strlen(process_title) + 1;
if (size < len)
return -ENOBUFS;
memcpy(buffer, process_title, len);
} else {
len = 0;
}
buffer[len] = '\0';
return 0;
}
int uv_resident_set_memory(size_t* rss) {
struct kinfo_proc kinfo;
size_t page_size = getpagesize();
size_t size = sizeof(struct kinfo_proc);
int mib[6];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
mib[4] = sizeof(struct kinfo_proc);
mib[5] = 1;
if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0)
return -errno;
*rss = kinfo.p_vm_rssize * page_size;
return 0;
}
int uv_uptime(double* uptime) {
time_t now;
struct timeval info;
size_t size = sizeof(info);
static int which[] = {CTL_KERN, KERN_BOOTTIME};
if (sysctl(which, 2, &info, &size, NULL, 0))
return -errno;
now = time(NULL);
*uptime = (double)(now - info.tv_sec);
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
multiplier = ((uint64_t)1000L / ticks), cpuspeed;
uint64_t info[CPUSTATES];
char model[512];
int numcpus = 1;
int which[] = {CTL_HW,HW_MODEL,0};
size_t size;
int i;
uv_cpu_info_t* cpu_info;
size = sizeof(model);
if (sysctl(which, 2, &model, &size, NULL, 0))
return -errno;
which[1] = HW_NCPU;
size = sizeof(numcpus);
if (sysctl(which, 2, &numcpus, &size, NULL, 0))
return -errno;
*cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
if (!(*cpu_infos))
return -ENOMEM;
*count = numcpus;
which[1] = HW_CPUSPEED;
size = sizeof(cpuspeed);
if (sysctl(which, 2, &cpuspeed, &size, NULL, 0)) {
uv__free(*cpu_infos);
return -errno;
}
size = sizeof(info);
which[0] = CTL_KERN;
which[1] = KERN_CPTIME2;
for (i = 0; i < numcpus; i++) {
which[2] = i;
size = sizeof(info);
if (sysctl(which, 3, &info, &size, NULL, 0)) {
uv__free(*cpu_infos);
return -errno;
}
cpu_info = &(*cpu_infos)[i];
cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier;
cpu_info->cpu_times.nice = (uint64_t)(info[CP_NICE]) * multiplier;
cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier;
cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier;
cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier;
cpu_info->model = uv__strdup(model);
cpu_info->speed = cpuspeed;
}
return 0;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(cpu_infos[i].model);
}
uv__free(cpu_infos);
}
@@ -0,0 +1,334 @@
/* Copyright libuv project 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 "os390-syscalls.h"
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <search.h>
#define CW_CONDVAR 32
#pragma linkage(BPX4CTW, OS)
#pragma linkage(BPX1CTW, OS)
static int number_of_epolls;
static QUEUE global_epoll_queue;
static uv_mutex_t global_epoll_lock;
static uv_once_t once = UV_ONCE_INIT;
int scandir(const char* maindir, struct dirent*** namelist,
int (*filter)(const struct dirent*),
int (*compar)(const struct dirent**,
const struct dirent **)) {
struct dirent** nl;
struct dirent* dirent;
unsigned count;
size_t allocated;
DIR* mdir;
nl = NULL;
count = 0;
allocated = 0;
mdir = opendir(maindir);
if (!mdir)
return -1;
while (1) {
dirent = readdir(mdir);
if (!dirent)
break;
if (!filter || filter(dirent)) {
struct dirent* copy;
copy = uv__malloc(sizeof(*copy));
if (!copy) {
while (count) {
dirent = nl[--count];
uv__free(dirent);
}
uv__free(nl);
closedir(mdir);
errno = ENOMEM;
return -1;
}
memcpy(copy, dirent, sizeof(*copy));
nl = uv__realloc(nl, sizeof(*copy) * (count + 1));
nl[count++] = copy;
}
}
qsort(nl, count, sizeof(struct dirent *),
(int (*)(const void *, const void *)) compar);
closedir(mdir);
*namelist = nl;
return count;
}
static unsigned int next_power_of_two(unsigned int val) {
val -= 1;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
val += 1;
return val;
}
static void maybe_resize(uv__os390_epoll* lst, unsigned int len) {
unsigned int newsize;
unsigned int i;
struct pollfd* newlst;
if (len <= lst->size)
return;
newsize = next_power_of_two(len);
newlst = uv__realloc(lst->items, newsize * sizeof(lst->items[0]));
if (newlst == NULL)
abort();
for (i = lst->size; i < newsize; ++i)
newlst[i].fd = -1;
lst->items = newlst;
lst->size = newsize;
}
static void epoll_init(void) {
QUEUE_INIT(&global_epoll_queue);
if (uv_mutex_init(&global_epoll_lock))
abort();
}
uv__os390_epoll* epoll_create1(int flags) {
uv__os390_epoll* lst;
uv_once(&once, epoll_init);
uv_mutex_lock(&global_epoll_lock);
lst = uv__malloc(sizeof(*lst));
if (lst == -1)
return NULL;
QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member);
uv_mutex_unlock(&global_epoll_lock);
/* initialize list */
lst->size = 0;
lst->items = NULL;
return lst;
}
int epoll_ctl(uv__os390_epoll* lst,
int op,
int fd,
struct epoll_event *event) {
if(op == EPOLL_CTL_DEL) {
if (fd >= lst->size || lst->items[fd].fd == -1) {
errno = ENOENT;
return -1;
}
lst->items[fd].fd = -1;
} else if(op == EPOLL_CTL_ADD) {
maybe_resize(lst, fd + 1);
if (lst->items[fd].fd != -1) {
errno = EEXIST;
return -1;
}
lst->items[fd].fd = fd;
lst->items[fd].events = event->events;
} else if(op == EPOLL_CTL_MOD) {
if (fd >= lst->size || lst->items[fd].fd == -1) {
errno = ENOENT;
return -1;
}
lst->items[fd].events = event->events;
} else
abort();
return 0;
}
int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
int maxevents, int timeout) {
size_t size;
struct pollfd* pfds;
int pollret;
int reventcount;
uv_mutex_lock(&global_epoll_lock);
uv_mutex_unlock(&global_epoll_lock);
size = lst->size;
pfds = lst->items;
pollret = poll(pfds, size, timeout);
if(pollret == -1)
return pollret;
reventcount = 0;
for (int i = 0; i < lst->size && i < maxevents; ++i) {
struct epoll_event ev;
ev.events = 0;
ev.fd = pfds[i].fd;
if(!pfds[i].revents)
continue;
if(pfds[i].revents & POLLRDNORM)
ev.events = ev.events | POLLIN;
if(pfds[i].revents & POLLWRNORM)
ev.events = ev.events | POLLOUT;
if(pfds[i].revents & POLLHUP)
ev.events = ev.events | POLLHUP;
pfds[i].revents = 0;
events[reventcount++] = ev;
}
return reventcount;
}
int epoll_file_close(int fd) {
QUEUE* q;
uv_once(&once, epoll_init);
uv_mutex_lock(&global_epoll_lock);
QUEUE_FOREACH(q, &global_epoll_queue) {
uv__os390_epoll* lst;
lst = QUEUE_DATA(q, uv__os390_epoll, member);
if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1)
lst->items[fd].fd = -1;
}
uv_mutex_unlock(&global_epoll_lock);
return 0;
}
void epoll_queue_close(uv__os390_epoll* lst) {
uv_mutex_lock(&global_epoll_lock);
QUEUE_REMOVE(&lst->member);
uv_mutex_unlock(&global_epoll_lock);
uv__free(lst->items);
lst->items = NULL;
}
int nanosleep(const struct timespec* req, struct timespec* rem) {
unsigned nano;
unsigned seconds;
unsigned events;
unsigned secrem;
unsigned nanorem;
int rv;
int rc;
int rsn;
nano = (int)req->tv_nsec;
seconds = req->tv_sec;
events = CW_CONDVAR;
#if defined(_LP64)
BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn);
#else
BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &rc, &rsn);
#endif
assert(rv == -1 && errno == EAGAIN);
if(rem != NULL) {
rem->tv_nsec = nanorem;
rem->tv_sec = secrem;
}
return 0;
}
char* mkdtemp(char* path) {
static const char* tempchars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static const size_t num_chars = 62;
static const size_t num_x = 6;
char *ep, *cp;
unsigned int tries, i;
size_t len;
uint64_t v;
int fd;
int retval;
int saved_errno;
len = strlen(path);
ep = path + len;
if (len < num_x || strncmp(ep - num_x, "XXXXXX", num_x)) {
errno = EINVAL;
return NULL;
}
fd = open("/dev/urandom", O_RDONLY);
if (fd == -1)
return NULL;
tries = TMP_MAX;
retval = -1;
do {
if (read(fd, &v, sizeof(v)) != sizeof(v))
break;
cp = ep - num_x;
for (i = 0; i < num_x; i++) {
*cp++ = tempchars[v % num_chars];
v /= num_chars;
}
if (mkdir(path, S_IRWXU) == 0) {
retval = 0;
break;
}
else if (errno != EEXIST)
break;
} while (--tries);
saved_errno = errno;
uv__close(fd);
if (tries == 0) {
errno = EEXIST;
return NULL;
}
if (retval == -1) {
errno = saved_errno;
return NULL;
}
return path;
}
@@ -0,0 +1,69 @@
/* Copyright libuv project 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.
*/
#ifndef UV_OS390_SYSCALL_H_
#define UV_OS390_SYSCALL_H_
#include "uv.h"
#include "internal.h"
#include <dirent.h>
#include <poll.h>
#include <pthread.h>
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3
#define MAX_EPOLL_INSTANCES 256
#define MAX_ITEMS_PER_EPOLL 1024
#define UV__O_CLOEXEC 0x80000
#define UV__EPOLL_CLOEXEC UV__O_CLOEXEC
#define UV__EPOLL_CTL_ADD EPOLL_CTL_ADD
#define UV__EPOLL_CTL_DEL EPOLL_CTL_DEL
#define UV__EPOLL_CTL_MOD EPOLL_CTL_MOD
struct epoll_event {
int events;
int fd;
};
typedef struct {
QUEUE member;
struct pollfd* items;
unsigned long size;
} uv__os390_epoll;
/* epoll api */
uv__os390_epoll* epoll_create1(int flags);
int epoll_ctl(uv__os390_epoll* ep, int op, int fd, struct epoll_event *event);
int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, int timeout);
int epoll_file_close(int fd);
/* utility functions */
int nanosleep(const struct timespec* req, struct timespec* rem);
int scandir(const char* maindir, struct dirent*** namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **,
const struct dirent **));
char *mkdtemp(char* path);
#endif /* UV_OS390_SYSCALL_H_ */
@@ -0,0 +1,849 @@
/* Copyright libuv project 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 "internal.h"
#include <sys/ioctl.h>
#include <net/if.h>
#include <utmpx.h>
#include <unistd.h>
#include <sys/ps.h>
#if defined(__clang__)
#include "csrsic.h"
#else
#include "//'SYS1.SAMPLIB(CSRSIC)'"
#endif
#define CVT_PTR 0x10
#define CSD_OFFSET 0x294
/*
Long-term average CPU service used by this logical partition,
in millions of service units per hour. If this value is above
the partition's defined capacity, the partition will be capped.
It is calculated using the physical CPU adjustment factor
(RCTPCPUA) so it may not match other measures of service which
are based on the logical CPU adjustment factor. It is available
if the hardware supports LPAR cluster.
*/
#define RCTLACS_OFFSET 0xC4
/* 32-bit count of alive CPUs. This includes both CPs and IFAs */
#define CSD_NUMBER_ONLINE_CPUS 0xD4
/* Address of system resources manager (SRM) control table */
#define CVTOPCTP_OFFSET 0x25C
/* Address of the RCT table */
#define RMCTRCT_OFFSET 0xE4
/* Address of the rsm control and enumeration area. */
#define CVTRCEP_OFFSET 0x490
/*
Number of frames currently available to system.
Excluded are frames backing perm storage, frames offline, and bad frames.
*/
#define RCEPOOL_OFFSET 0x004
/* Total number of frames currently on all available frame queues. */
#define RCEAFC_OFFSET 0x088
/* CPC model length from the CSRSI Service. */
#define CPCMODEL_LENGTH 16
/* Thread Entry constants */
#define PGTH_CURRENT 1
#define PGTH_LEN 26
#define PGTHAPATH 0x20
#pragma linkage(BPX4GTH, OS)
#pragma linkage(BPX1GTH, OS)
typedef unsigned data_area_ptr_assign_type;
typedef union {
struct {
#if defined(_LP64)
data_area_ptr_assign_type lower;
#endif
data_area_ptr_assign_type assign;
};
char* deref;
} data_area_ptr;
void uv_loadavg(double avg[3]) {
/* TODO: implement the following */
avg[0] = 0;
avg[1] = 0;
avg[2] = 0;
}
int uv__platform_loop_init(uv_loop_t* loop) {
uv__os390_epoll* ep;
ep = epoll_create1(UV__EPOLL_CLOEXEC);
loop->ep = ep;
if (ep == NULL)
return -errno;
return 0;
}
void uv__platform_loop_delete(uv_loop_t* loop) {
if (loop->ep != NULL) {
epoll_queue_close(loop->ep);
loop->ep = NULL;
}
}
uint64_t uv__hrtime(uv_clocktype_t type) {
struct timeval time;
gettimeofday(&time, NULL);
return (uint64_t) time.tv_sec * 1e9 + time.tv_usec * 1e3;
}
/*
Get the exe path using the thread entry information
in the address space.
*/
static int getexe(const int pid, char* buf, size_t len) {
struct {
int pid;
int thid[2];
char accesspid;
char accessthid;
char asid[2];
char loginname[8];
char flag;
char len;
} Input_data;
union {
struct {
char gthb[4];
int pid;
int thid[2];
char accesspid;
char accessthid[3];
int lenused;
int offsetProcess;
int offsetConTTY;
int offsetPath;
int offsetCommand;
int offsetFileData;
int offsetThread;
} Output_data;
char buf[2048];
} Output_buf;
struct Output_path_type {
char gthe[4];
short int len;
char path[1024];
};
int Input_length;
int Output_length;
void* Input_address;
void* Output_address;
struct Output_path_type* Output_path;
int rv;
int rc;
int rsn;
Input_length = PGTH_LEN;
Output_length = sizeof(Output_buf);
Output_address = &Output_buf;
Input_address = &Input_data;
memset(&Input_data, 0, sizeof Input_data);
Input_data.flag |= PGTHAPATH;
Input_data.pid = pid;
Input_data.accesspid = PGTH_CURRENT;
#ifdef _LP64
BPX4GTH(&Input_length,
&Input_address,
&Output_length,
&Output_address,
&rv,
&rc,
&rsn);
#else
BPX1GTH(&Input_length,
&Input_address,
&Output_length,
&Output_address,
&rv,
&rc,
&rsn);
#endif
if (rv == -1) {
errno = rc;
return -1;
}
/* Check highest byte to ensure data availability */
assert(((Output_buf.Output_data.offsetPath >>24) & 0xFF) == 'A');
/* Get the offset from the lowest 3 bytes */
Output_path = (char*)(&Output_buf) +
(Output_buf.Output_data.offsetPath & 0x00FFFFFF);
if (Output_path->len >= len) {
errno = ENOBUFS;
return -1;
}
strncpy(buf, Output_path->path, len);
return 0;
}
/*
* We could use a static buffer for the path manipulations that we need outside
* of the function, but this function could be called by multiple consumers and
* we don't want to potentially create a race condition in the use of snprintf.
* There is no direct way of getting the exe path in zOS - either through /procfs
* or through some libc APIs. The below approach is to parse the argv[0]'s pattern
* and use it in conjunction with PATH environment variable to craft one.
*/
int uv_exepath(char* buffer, size_t* size) {
int res;
char args[PATH_MAX];
char abspath[PATH_MAX];
size_t abspath_size;
int pid;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
pid = getpid();
res = getexe(pid, args, sizeof(args));
if (res < 0)
return -EINVAL;
/*
* Possibilities for args:
* i) an absolute path such as: /home/user/myprojects/nodejs/node
* ii) a relative path such as: ./node or ../myprojects/nodejs/node
* iii) a bare filename such as "node", after exporting PATH variable
* to its location.
*/
/* Case i) and ii) absolute or relative paths */
if (strchr(args, '/') != NULL) {
if (realpath(args, abspath) != abspath)
return -errno;
abspath_size = strlen(abspath);
*size -= 1;
if (*size > abspath_size)
*size = abspath_size;
memcpy(buffer, abspath, *size);
buffer[*size] = '\0';
return 0;
} else {
/* Case iii). Search PATH environment variable */
char trypath[PATH_MAX];
char* clonedpath = NULL;
char* token = NULL;
char* path = getenv("PATH");
if (path == NULL)
return -EINVAL;
clonedpath = uv__strdup(path);
if (clonedpath == NULL)
return -ENOMEM;
token = strtok(clonedpath, ":");
while (token != NULL) {
snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args);
if (realpath(trypath, abspath) == abspath) {
/* Check the match is executable */
if (access(abspath, X_OK) == 0) {
abspath_size = strlen(abspath);
*size -= 1;
if (*size > abspath_size)
*size = abspath_size;
memcpy(buffer, abspath, *size);
buffer[*size] = '\0';
uv__free(clonedpath);
return 0;
}
}
token = strtok(NULL, ":");
}
uv__free(clonedpath);
/* Out of tokens (path entries), and no match found */
return -EINVAL;
}
}
uint64_t uv_get_free_memory(void) {
uint64_t freeram;
data_area_ptr cvt = {0};
data_area_ptr rcep = {0};
cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR);
rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET);
freeram = *((uint64_t*)(rcep.deref + RCEAFC_OFFSET)) * 4;
return freeram;
}
uint64_t uv_get_total_memory(void) {
uint64_t totalram;
data_area_ptr cvt = {0};
data_area_ptr rcep = {0};
cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR);
rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET);
totalram = *((uint64_t*)(rcep.deref + RCEPOOL_OFFSET)) * 4;
return totalram;
}
int uv_resident_set_memory(size_t* rss) {
W_PSPROC buf;
memset(&buf, 0, sizeof(buf));
if (w_getpsent(0, &buf, sizeof(W_PSPROC)) == -1)
return -EINVAL;
*rss = buf.ps_size;
return 0;
}
int uv_uptime(double* uptime) {
struct utmpx u ;
struct utmpx *v;
time64_t t;
u.ut_type = BOOT_TIME;
v = getutxid(&u);
if (v == NULL)
return -1;
*uptime = difftime64(time64(&t), v->ut_tv.tv_sec);
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
uv_cpu_info_t* cpu_info;
int result;
int idx;
siv1v2 info;
data_area_ptr cvt = {0};
data_area_ptr csd = {0};
data_area_ptr rmctrct = {0};
data_area_ptr cvtopctp = {0};
int cpu_usage_avg;
cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR);
csd.assign = *((data_area_ptr_assign_type *) (cvt.deref + CSD_OFFSET));
cvtopctp.assign = *((data_area_ptr_assign_type *) (cvt.deref + CVTOPCTP_OFFSET));
rmctrct.assign = *((data_area_ptr_assign_type *) (cvtopctp.deref + RMCTRCT_OFFSET));
*count = *((int*) (csd.deref + CSD_NUMBER_ONLINE_CPUS));
cpu_usage_avg = *((unsigned short int*) (rmctrct.deref + RCTLACS_OFFSET));
*cpu_infos = uv__malloc(*count * sizeof(uv_cpu_info_t));
if (!*cpu_infos)
return -ENOMEM;
cpu_info = *cpu_infos;
idx = 0;
while (idx < *count) {
cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability);
cpu_info->model = uv__malloc(CPCMODEL_LENGTH + 1);
memset(cpu_info->model, '\0', CPCMODEL_LENGTH + 1);
memcpy(cpu_info->model, info.siv1v2si11v1.si11v1cpcmodel, CPCMODEL_LENGTH);
cpu_info->cpu_times.user = cpu_usage_avg;
/* TODO: implement the following */
cpu_info->cpu_times.sys = 0;
cpu_info->cpu_times.idle = 0;
cpu_info->cpu_times.irq = 0;
cpu_info->cpu_times.nice = 0;
++cpu_info;
++idx;
}
return 0;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
for (int i = 0; i < count; ++i)
uv__free(cpu_infos[i].model);
uv__free(cpu_infos);
}
static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
int* count) {
uv_interface_address_t* address;
int sockfd;
int maxsize;
__net_ifconf6header_t ifc;
__net_ifconf6entry_t* ifr;
__net_ifconf6entry_t* p;
__net_ifconf6entry_t flg;
*count = 0;
/* Assume maximum buffer size allowable */
maxsize = 16384;
if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)))
return -errno;
ifc.__nif6h_version = 1;
ifc.__nif6h_buflen = maxsize;
ifc.__nif6h_buffer = uv__calloc(1, maxsize);;
if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) {
uv__close(sockfd);
return -errno;
}
*count = 0;
ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
p = ifr;
ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
if (!(p->__nif6e_addr.sin6_family == AF_INET6 ||
p->__nif6e_addr.sin6_family == AF_INET))
continue;
if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
continue;
++(*count);
}
/* Alloc the return interface structs */
*addresses = uv__malloc(*count * sizeof(uv_interface_address_t));
if (!(*addresses)) {
uv__close(sockfd);
return -ENOMEM;
}
address = *addresses;
ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
p = ifr;
ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
if (!(p->__nif6e_addr.sin6_family == AF_INET6 ||
p->__nif6e_addr.sin6_family == AF_INET))
continue;
if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
continue;
/* All conditions above must match count loop */
address->name = uv__strdup(p->__nif6e_name);
if (p->__nif6e_addr.sin6_family == AF_INET6)
address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr);
else
address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr);
/* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */
address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0;
address++;
}
uv__close(sockfd);
return 0;
}
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
uv_interface_address_t* address;
int sockfd;
int maxsize;
struct ifconf ifc;
struct ifreq flg;
struct ifreq* ifr;
struct ifreq* p;
int count_v6;
/* get the ipv6 addresses first */
uv_interface_address_t* addresses_v6;
uv__interface_addresses_v6(&addresses_v6, &count_v6);
/* now get the ipv4 addresses */
*count = 0;
/* Assume maximum buffer size allowable */
maxsize = 16384;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (0 > sockfd)
return -errno;
ifc.ifc_req = uv__calloc(1, maxsize);
ifc.ifc_len = maxsize;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
uv__close(sockfd);
return -errno;
}
#define MAX(a,b) (((a)>(b))?(a):(b))
#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
/* Count all up and running ipv4/ipv6 addresses */
ifr = ifc.ifc_req;
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
p = ifr;
ifr = (struct ifreq*)
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
if (!(p->ifr_addr.sa_family == AF_INET6 ||
p->ifr_addr.sa_family == AF_INET))
continue;
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
uv__close(sockfd);
return -errno;
}
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
continue;
(*count)++;
}
/* Alloc the return interface structs */
*addresses = uv__malloc((*count + count_v6) *
sizeof(uv_interface_address_t));
if (!(*addresses)) {
uv__close(sockfd);
return -ENOMEM;
}
address = *addresses;
/* copy over the ipv6 addresses */
memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t));
address += count_v6;
*count += count_v6;
uv__free(addresses_v6);
ifr = ifc.ifc_req;
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
p = ifr;
ifr = (struct ifreq*)
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
if (!(p->ifr_addr.sa_family == AF_INET6 ||
p->ifr_addr.sa_family == AF_INET))
continue;
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
uv__close(sockfd);
return -ENOSYS;
}
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
continue;
/* All conditions above must match count loop */
address->name = uv__strdup(p->ifr_name);
if (p->ifr_addr.sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
} else {
address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
}
address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0;
address++;
}
#undef ADDR_SIZE
#undef MAX
uv__close(sockfd);
return 0;
}
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
int i;
for (i = 0; i < count; ++i)
uv__free(addresses[i].name);
uv__free(addresses);
}
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
struct epoll_event* events;
struct epoll_event dummy;
uintptr_t i;
uintptr_t nfds;
assert(loop->watchers != NULL);
events = (struct epoll_event*) loop->watchers[loop->nwatchers];
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
if (events != NULL)
/* Invalidate events with same file descriptor */
for (i = 0; i < nfds; i++)
if ((int) events[i].fd == fd)
events[i].fd = -1;
/* Remove the file descriptor from the epoll. */
if (loop->ep != NULL)
epoll_ctl(loop->ep, UV__EPOLL_CTL_DEL, fd, &dummy);
}
int uv__io_check_fd(uv_loop_t* loop, int fd) {
struct pollfd p[1];
int rv;
p[0].fd = fd;
p[0].events = POLLIN;
do
rv = poll(p, 1, 0);
while (rv == -1 && errno == EINTR);
if (rv == -1)
abort();
if (p[0].revents & POLLNVAL)
return -1;
return 0;
}
void uv__io_poll(uv_loop_t* loop, int timeout) {
static const int max_safe_timeout = 1789569;
struct epoll_event events[1024];
struct epoll_event* pe;
struct epoll_event e;
int real_timeout;
QUEUE* q;
uv__io_t* w;
uint64_t base;
int count;
int nfds;
int fd;
int op;
int i;
if (loop->nfds == 0) {
assert(QUEUE_EMPTY(&loop->watcher_queue));
return;
}
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
uv_stream_t* stream;
q = QUEUE_HEAD(&loop->watcher_queue);
QUEUE_REMOVE(q);
QUEUE_INIT(q);
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
assert(w->pevents != 0);
assert(w->fd >= 0);
stream= container_of(w, uv_stream_t, io_watcher);
assert(w->fd < (int) loop->nwatchers);
e.events = w->pevents;
e.fd = w->fd;
if (w->events == 0)
op = UV__EPOLL_CTL_ADD;
else
op = UV__EPOLL_CTL_MOD;
/* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
* events, skip the syscall and squelch the events after epoll_wait().
*/
if (epoll_ctl(loop->ep, op, w->fd, &e)) {
if (errno != EEXIST)
abort();
assert(op == UV__EPOLL_CTL_ADD);
/* We've reactivated a file descriptor that's been watched before. */
if (epoll_ctl(loop->ep, UV__EPOLL_CTL_MOD, w->fd, &e))
abort();
}
w->events = w->pevents;
}
assert(timeout >= -1);
base = loop->time;
count = 48; /* Benchmarks suggest this gives the best throughput. */
real_timeout = timeout;
int nevents = 0;
nfds = 0;
for (;;) {
if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
timeout = max_safe_timeout;
nfds = epoll_wait(loop->ep, events,
ARRAY_SIZE(events), timeout);
/* Update loop->time unconditionally. It's tempting to skip the update when
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
* operating system didn't reschedule our process while in the syscall.
*/
base = loop->time;
SAVE_ERRNO(uv__update_time(loop));
if (nfds == 0) {
assert(timeout != -1);
timeout = real_timeout - timeout;
if (timeout > 0)
continue;
return;
}
if (nfds == -1) {
if (errno != EINTR)
abort();
if (timeout == -1)
continue;
if (timeout == 0)
return;
/* Interrupted by a signal. Update timeout and poll again. */
goto update_timeout;
}
assert(loop->watchers != NULL);
loop->watchers[loop->nwatchers] = (void*) events;
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
for (i = 0; i < nfds; i++) {
pe = events + i;
fd = pe->fd;
/* Skip invalidated events, see uv__platform_invalidate_fd */
if (fd == -1)
continue;
assert(fd >= 0);
assert((unsigned) fd < loop->nwatchers);
w = loop->watchers[fd];
if (w == NULL) {
/* File descriptor that we've stopped watching, disarm it.
*
* Ignore all errors because we may be racing with another thread
* when the file descriptor is closed.
*/
epoll_ctl(loop->ep, UV__EPOLL_CTL_DEL, fd, pe);
continue;
}
/* Give users only events they're interested in. Prevents spurious
* callbacks when previous callback invocation in this loop has stopped
* the current watcher. Also, filters out events that users has not
* requested us to watch.
*/
pe->events &= w->pevents | POLLERR | POLLHUP;
if (pe->events == POLLERR || pe->events == POLLHUP)
pe->events |= w->pevents & (POLLIN | POLLOUT);
if (pe->events != 0) {
w->cb(loop, w, pe->events);
nevents++;
}
}
loop->watchers[loop->nwatchers] = NULL;
loop->watchers[loop->nwatchers + 1] = NULL;
if (nevents != 0) {
if (nfds == ARRAY_SIZE(events) && --count != 0) {
/* Poll for more events but don't block this time. */
timeout = 0;
continue;
}
return;
}
if (timeout == 0)
return;
if (timeout == -1)
continue;
update_timeout:
assert(timeout > 0);
real_timeout -= (loop->time - base);
if (real_timeout <= 0)
return;
timeout = real_timeout;
}
}
void uv__set_process_title(const char* title) {
/* do nothing */
}
int uv__io_fork(uv_loop_t* loop) {
uv__platform_loop_delete(loop);
return uv__platform_loop_init(loop);
}
@@ -0,0 +1,304 @@
/* 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 "internal.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
handle->shutdown_req = NULL;
handle->connect_req = NULL;
handle->pipe_fname = NULL;
handle->ipc = ipc;
return 0;
}
int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
struct sockaddr_un saddr;
const char* pipe_fname;
int sockfd;
int err;
pipe_fname = NULL;
/* Already bound? */
if (uv__stream_fd(handle) >= 0)
return -EINVAL;
/* Make a copy of the file name, it outlives this function's scope. */
pipe_fname = uv__strdup(name);
if (pipe_fname == NULL)
return -ENOMEM;
/* We've got a copy, don't touch the original any more. */
name = NULL;
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
if (err < 0)
goto err_socket;
sockfd = err;
memset(&saddr, 0, sizeof saddr);
strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1);
saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
saddr.sun_family = AF_UNIX;
if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
err = -errno;
/* Convert ENOENT to EACCES for compatibility with Windows. */
if (err == -ENOENT)
err = -EACCES;
uv__close(sockfd);
goto err_socket;
}
/* Success. */
handle->flags |= UV_HANDLE_BOUND;
handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */
handle->io_watcher.fd = sockfd;
return 0;
err_socket:
uv__free((void*)pipe_fname);
return err;
}
int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
if (uv__stream_fd(handle) == -1)
return -EINVAL;
#if defined(__MVS__)
/* On zOS, backlog=0 has undefined behaviour */
if (backlog == 0)
backlog = 1;
else if (backlog < 0)
backlog = SOMAXCONN;
#endif
if (listen(uv__stream_fd(handle), backlog))
return -errno;
handle->connection_cb = cb;
handle->io_watcher.cb = uv__server_io;
uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
return 0;
}
void uv__pipe_close(uv_pipe_t* handle) {
if (handle->pipe_fname) {
/*
* Unlink the file system entity before closing the file descriptor.
* Doing it the other way around introduces a race where our process
* unlinks a socket with the same name that's just been created by
* another thread or process.
*/
unlink(handle->pipe_fname);
uv__free((void*)handle->pipe_fname);
handle->pipe_fname = NULL;
}
uv__stream_close((uv_stream_t*)handle);
}
int uv_pipe_open(uv_pipe_t* handle, uv_file fd) {
int err;
err = uv__nonblock(fd, 1);
if (err)
return err;
#if defined(__APPLE__)
err = uv__stream_try_select((uv_stream_t*) handle, &fd);
if (err)
return err;
#endif /* defined(__APPLE__) */
return uv__stream_open((uv_stream_t*)handle,
fd,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
}
void uv_pipe_connect(uv_connect_t* req,
uv_pipe_t* handle,
const char* name,
uv_connect_cb cb) {
struct sockaddr_un saddr;
int new_sock;
int err;
int r;
new_sock = (uv__stream_fd(handle) == -1);
if (new_sock) {
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
if (err < 0)
goto out;
handle->io_watcher.fd = err;
}
memset(&saddr, 0, sizeof saddr);
strncpy(saddr.sun_path, name, sizeof(saddr.sun_path) - 1);
saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
saddr.sun_family = AF_UNIX;
do {
r = connect(uv__stream_fd(handle),
(struct sockaddr*)&saddr, sizeof saddr);
}
while (r == -1 && errno == EINTR);
if (r == -1 && errno != EINPROGRESS) {
err = -errno;
#if defined(__CYGWIN__) || defined(__MSYS__)
/* EBADF is supposed to mean that the socket fd is bad, but
Cygwin reports EBADF instead of ENOTSOCK when the file is
not a socket. We do not expect to see a bad fd here
(e.g. due to new_sock), so translate the error. */
if (err == -EBADF)
err = -ENOTSOCK;
#endif
goto out;
}
err = 0;
if (new_sock) {
err = uv__stream_open((uv_stream_t*)handle,
uv__stream_fd(handle),
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
}
if (err == 0)
uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT);
out:
handle->delayed_error = err;
handle->connect_req = req;
uv__req_init(handle->loop, req, UV_CONNECT);
req->handle = (uv_stream_t*)handle;
req->cb = cb;
QUEUE_INIT(&req->queue);
/* Force callback to run on next tick in case of error. */
if (err)
uv__io_feed(handle->loop, &handle->io_watcher);
}
typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
uv__peersockfunc func,
char* buffer,
size_t* size) {
struct sockaddr_un sa;
socklen_t addrlen;
int err;
addrlen = sizeof(sa);
memset(&sa, 0, addrlen);
err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen);
if (err < 0) {
*size = 0;
return -errno;
}
#if defined(__linux__)
if (sa.sun_path[0] == 0)
/* Linux abstract namespace */
addrlen -= offsetof(struct sockaddr_un, sun_path);
else
#endif
addrlen = strlen(sa.sun_path);
if (addrlen >= *size) {
*size = addrlen + 1;
return UV_ENOBUFS;
}
memcpy(buffer, sa.sun_path, addrlen);
*size = addrlen;
/* only null-terminate if it's not an abstract socket */
if (buffer[0] != '\0')
buffer[addrlen] = '\0';
return 0;
}
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
return uv__pipe_getsockpeername(handle, getsockname, buffer, size);
}
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
return uv__pipe_getsockpeername(handle, getpeername, buffer, size);
}
void uv_pipe_pending_instances(uv_pipe_t* handle, int count) {
}
int uv_pipe_pending_count(uv_pipe_t* handle) {
uv__stream_queued_fds_t* queued_fds;
if (!handle->ipc)
return 0;
if (handle->accepted_fd == -1)
return 0;
if (handle->queued_fds == NULL)
return 1;
queued_fds = handle->queued_fds;
return queued_fds->offset + 1;
}
uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
if (!handle->ipc)
return UV_UNKNOWN_HANDLE;
if (handle->accepted_fd == -1)
return UV_UNKNOWN_HANDLE;
else
return uv__handle_type(handle->accepted_fd);
}
@@ -0,0 +1,146 @@
/* 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 "internal.h"
#include <unistd.h>
#include <assert.h>
#include <errno.h>
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
uv_poll_t* handle;
int pevents;
handle = container_of(w, uv_poll_t, io_watcher);
/*
* As documented in the kernel source fs/kernfs/file.c #780
* poll will return POLLERR|POLLPRI in case of sysfs
* polling. This does not happen in case of out-of-band
* TCP messages.
*
* The above is the case on (at least) FreeBSD and Linux.
*
* So to properly determine a POLLPRI or a POLLERR we need
* to check for both.
*/
if ((events & POLLERR) && !(events & UV__POLLPRI)) {
uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
uv__handle_stop(handle);
handle->poll_cb(handle, -EBADF, 0);
return;
}
pevents = 0;
if (events & POLLIN)
pevents |= UV_READABLE;
if (events & UV__POLLPRI)
pevents |= UV_PRIORITIZED;
if (events & POLLOUT)
pevents |= UV_WRITABLE;
if (events & UV__POLLRDHUP)
pevents |= UV_DISCONNECT;
handle->poll_cb(handle, 0, pevents);
}
int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
int err;
err = uv__io_check_fd(loop, fd);
if (err)
return err;
/* If ioctl(FIONBIO) reports ENOTTY, try fcntl(F_GETFL) + fcntl(F_SETFL).
* Workaround for e.g. kqueue fds not supporting ioctls.
*/
err = uv__nonblock(fd, 1);
if (err == -ENOTTY)
if (uv__nonblock == uv__nonblock_ioctl)
err = uv__nonblock_fcntl(fd, 1);
if (err)
return err;
uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
uv__io_init(&handle->io_watcher, uv__poll_io, fd);
handle->poll_cb = NULL;
return 0;
}
int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
uv_os_sock_t socket) {
return uv_poll_init(loop, handle, socket);
}
static void uv__poll_stop(uv_poll_t* handle) {
uv__io_stop(handle->loop,
&handle->io_watcher,
POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
uv__handle_stop(handle);
}
int uv_poll_stop(uv_poll_t* handle) {
assert(!uv__is_closing(handle));
uv__poll_stop(handle);
return 0;
}
int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
int events;
assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT |
UV_PRIORITIZED)) == 0);
assert(!uv__is_closing(handle));
uv__poll_stop(handle);
if (pevents == 0)
return 0;
events = 0;
if (pevents & UV_READABLE)
events |= POLLIN;
if (pevents & UV_PRIORITIZED)
events |= UV__POLLPRI;
if (pevents & UV_WRITABLE)
events |= POLLOUT;
if (pevents & UV_DISCONNECT)
events |= UV__POLLRDHUP;
uv__io_start(handle->loop, &handle->io_watcher, events);
uv__handle_start(handle);
handle->poll_cb = poll_cb;
return 0;
}
void uv__poll_close(uv_poll_t* handle) {
uv__poll_stop(handle);
}
@@ -0,0 +1,35 @@
/* Copyright libuv project 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 "internal.h"
#include <stdint.h>
#include <time.h>
#undef NANOSEC
#define NANOSEC ((uint64_t) 1e9)
uint64_t uv__hrtime(uv_clocktype_t type) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
}
@@ -0,0 +1,357 @@
/* Copyright libuv project 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 "internal.h"
/* POSIX defines poll() as a portable way to wait on file descriptors.
* Here we maintain a dynamically sized array of file descriptors and
* events to pass as the first argument to poll().
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
int uv__platform_loop_init(uv_loop_t* loop) {
loop->poll_fds = NULL;
loop->poll_fds_used = 0;
loop->poll_fds_size = 0;
loop->poll_fds_iterating = 0;
return 0;
}
void uv__platform_loop_delete(uv_loop_t* loop) {
uv__free(loop->poll_fds);
loop->poll_fds = NULL;
}
int uv__io_fork(uv_loop_t* loop) {
uv__platform_loop_delete(loop);
return uv__platform_loop_init(loop);
}
/* Allocate or dynamically resize our poll fds array. */
static void uv__pollfds_maybe_resize(uv_loop_t* loop) {
size_t i;
size_t n;
struct pollfd* p;
if (loop->poll_fds_used < loop->poll_fds_size)
return;
n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64;
p = uv__realloc(loop->poll_fds, n * sizeof(*loop->poll_fds));
if (p == NULL)
abort();
loop->poll_fds = p;
for (i = loop->poll_fds_size; i < n; i++) {
loop->poll_fds[i].fd = -1;
loop->poll_fds[i].events = 0;
loop->poll_fds[i].revents = 0;
}
loop->poll_fds_size = n;
}
/* Primitive swap operation on poll fds array elements. */
static void uv__pollfds_swap(uv_loop_t* loop, size_t l, size_t r) {
struct pollfd pfd;
pfd = loop->poll_fds[l];
loop->poll_fds[l] = loop->poll_fds[r];
loop->poll_fds[r] = pfd;
}
/* Add a watcher's fd to our poll fds array with its pending events. */
static void uv__pollfds_add(uv_loop_t* loop, uv__io_t* w) {
size_t i;
struct pollfd* pe;
/* If the fd is already in the set just update its events. */
assert(!loop->poll_fds_iterating);
for (i = 0; i < loop->poll_fds_used; ++i) {
if (loop->poll_fds[i].fd == w->fd) {
loop->poll_fds[i].events = w->pevents;
return;
}
}
/* Otherwise, allocate a new slot in the set for the fd. */
uv__pollfds_maybe_resize(loop);
pe = &loop->poll_fds[loop->poll_fds_used++];
pe->fd = w->fd;
pe->events = w->pevents;
}
/* Remove a watcher's fd from our poll fds array. */
static void uv__pollfds_del(uv_loop_t* loop, int fd) {
size_t i;
assert(!loop->poll_fds_iterating);
for (i = 0; i < loop->poll_fds_used; ++i) {
if (loop->poll_fds[i].fd == fd) {
/* swap to last position and remove */
--loop->poll_fds_used;
uv__pollfds_swap(loop, i, loop->poll_fds_used);
loop->poll_fds[loop->poll_fds_used].fd = -1;
loop->poll_fds[loop->poll_fds_used].events = 0;
loop->poll_fds[loop->poll_fds_used].revents = 0;
return;
}
}
}
void uv__io_poll(uv_loop_t* loop, int timeout) {
sigset_t* pset;
sigset_t set;
uint64_t time_base;
uint64_t time_diff;
QUEUE* q;
uv__io_t* w;
size_t i;
unsigned int nevents;
int nfds;
int have_signals;
struct pollfd* pe;
int fd;
if (loop->nfds == 0) {
assert(QUEUE_EMPTY(&loop->watcher_queue));
return;
}
/* Take queued watchers and add their fds to our poll fds array. */
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
q = QUEUE_HEAD(&loop->watcher_queue);
QUEUE_REMOVE(q);
QUEUE_INIT(q);
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
assert(w->pevents != 0);
assert(w->fd >= 0);
assert(w->fd < (int) loop->nwatchers);
uv__pollfds_add(loop, w);
w->events = w->pevents;
}
/* Prepare a set of signals to block around poll(), if any. */
pset = NULL;
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
pset = &set;
sigemptyset(pset);
sigaddset(pset, SIGPROF);
}
assert(timeout >= -1);
time_base = loop->time;
/* Loop calls to poll() and processing of results. If we get some
* results from poll() but they turn out not to be interesting to
* our caller then we need to loop around and poll() again.
*/
for (;;) {
if (pset != NULL)
if (pthread_sigmask(SIG_BLOCK, pset, NULL))
abort();
nfds = poll(loop->poll_fds, (nfds_t)loop->poll_fds_used, timeout);
if (pset != NULL)
if (pthread_sigmask(SIG_UNBLOCK, pset, NULL))
abort();
/* Update loop->time unconditionally. It's tempting to skip the update when
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
* operating system didn't reschedule our process while in the syscall.
*/
SAVE_ERRNO(uv__update_time(loop));
if (nfds == 0) {
assert(timeout != -1);
return;
}
if (nfds == -1) {
if (errno != EINTR)
abort();
if (timeout == -1)
continue;
if (timeout == 0)
return;
/* Interrupted by a signal. Update timeout and poll again. */
goto update_timeout;
}
/* Tell uv__platform_invalidate_fd not to manipulate our array
* while we are iterating over it.
*/
loop->poll_fds_iterating = 1;
/* Initialize a count of events that we care about. */
nevents = 0;
have_signals = 0;
/* Loop over the entire poll fds array looking for returned events. */
for (i = 0; i < loop->poll_fds_used; i++) {
pe = loop->poll_fds + i;
fd = pe->fd;
/* Skip invalidated events, see uv__platform_invalidate_fd. */
if (fd == -1)
continue;
assert(fd >= 0);
assert((unsigned) fd < loop->nwatchers);
w = loop->watchers[fd];
if (w == NULL) {
/* File descriptor that we've stopped watching, ignore. */
uv__platform_invalidate_fd(loop, fd);
continue;
}
/* Filter out events that user has not requested us to watch
* (e.g. POLLNVAL).
*/
pe->revents &= w->pevents | POLLERR | POLLHUP;
if (pe->revents != 0) {
/* Run signal watchers last. */
if (w == &loop->signal_io_watcher) {
have_signals = 1;
} else {
w->cb(loop, w, pe->revents);
}
nevents++;
}
}
if (have_signals != 0)
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
loop->poll_fds_iterating = 0;
/* Purge invalidated fds from our poll fds array. */
uv__pollfds_del(loop, -1);
if (have_signals != 0)
return; /* Event loop should cycle now so don't poll again. */
if (nevents != 0)
return;
if (timeout == 0)
return;
if (timeout == -1)
continue;
update_timeout:
assert(timeout > 0);
time_diff = loop->time - time_base;
if (time_diff >= (uint64_t) timeout)
return;
timeout -= time_diff;
}
}
/* Remove the given fd from our poll fds array because no one
* is interested in its events anymore.
*/
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
size_t i;
if (loop->poll_fds_iterating) {
/* uv__io_poll is currently iterating. Just invalidate fd. */
for (i = 0; i < loop->poll_fds_used; i++)
if (loop->poll_fds[i].fd == fd) {
loop->poll_fds[i].fd = -1;
loop->poll_fds[i].events = 0;
loop->poll_fds[i].revents = 0;
}
} else {
/* uv__io_poll is not iterating. Delete fd from the set. */
uv__pollfds_del(loop, fd);
}
}
/* Check whether the given fd is supported by poll(). */
int uv__io_check_fd(uv_loop_t* loop, int fd) {
struct pollfd p[1];
int rv;
p[0].fd = fd;
p[0].events = POLLIN;
do
rv = poll(p, 1, 0);
while (rv == -1 && (errno == EINTR || errno == EAGAIN));
if (rv == -1)
return -errno;
if (p[0].revents & POLLNVAL)
return -EINVAL;
return 0;
}
/*
* NEW
*/
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return 0;
uv__handle_stop(handle);
#if defined(__APPLE__)
if (uv__has_forked_with_cfrunloop || uv__fsevents_close(handle))
#endif /* defined(__APPLE__) */
{
uv__io_close(handle->loop, &handle->event_watcher);
}
uv__free(handle->path);
handle->path = NULL;
if (handle->event_watcher.fd != -1) {
/* When FSEvents is used, we don't use the event_watcher's fd under certain
* confitions. (see uv_fs_event_start) */
uv__close(handle->event_watcher.fd);
handle->event_watcher.fd = -1;
}
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}
@@ -0,0 +1,563 @@
/* 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 "internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#if defined(__APPLE__) && !TARGET_OS_IPHONE
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif
#if defined(__linux__) || defined(__GLIBC__)
# include <grp.h>
#endif
static void uv__chld(uv_signal_t* handle, int signum) {
uv_process_t* process;
uv_loop_t* loop;
int exit_status;
int term_signal;
int status;
pid_t pid;
QUEUE pending;
QUEUE* q;
QUEUE* h;
assert(signum == SIGCHLD);
QUEUE_INIT(&pending);
loop = handle->loop;
h = &loop->process_handles;
q = QUEUE_HEAD(h);
while (q != h) {
process = QUEUE_DATA(q, uv_process_t, queue);
q = QUEUE_NEXT(q);
do
pid = waitpid(process->pid, &status, WNOHANG);
while (pid == -1 && errno == EINTR);
if (pid == 0)
continue;
if (pid == -1) {
if (errno != ECHILD)
abort();
continue;
}
process->status = status;
QUEUE_REMOVE(&process->queue);
QUEUE_INSERT_TAIL(&pending, &process->queue);
}
h = &pending;
q = QUEUE_HEAD(h);
while (q != h) {
process = QUEUE_DATA(q, uv_process_t, queue);
q = QUEUE_NEXT(q);
QUEUE_REMOVE(&process->queue);
QUEUE_INIT(&process->queue);
uv__handle_stop(process);
if (process->exit_cb == NULL)
continue;
exit_status = 0;
if (WIFEXITED(process->status))
exit_status = WEXITSTATUS(process->status);
term_signal = 0;
if (WIFSIGNALED(process->status))
term_signal = WTERMSIG(process->status);
process->exit_cb(process, exit_status, term_signal);
}
assert(QUEUE_EMPTY(&pending));
}
int uv__make_socketpair(int fds[2], int flags) {
#if defined(__linux__)
static int no_cloexec;
if (no_cloexec)
goto skip;
if (socketpair(AF_UNIX, SOCK_STREAM | UV__SOCK_CLOEXEC | flags, 0, fds) == 0)
return 0;
/* Retry on EINVAL, it means SOCK_CLOEXEC is not supported.
* Anything else is a genuine error.
*/
if (errno != EINVAL)
return -errno;
no_cloexec = 1;
skip:
#endif
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
return -errno;
uv__cloexec(fds[0], 1);
uv__cloexec(fds[1], 1);
if (flags & UV__F_NONBLOCK) {
uv__nonblock(fds[0], 1);
uv__nonblock(fds[1], 1);
}
return 0;
}
int uv__make_pipe(int fds[2], int flags) {
#if defined(__linux__)
static int no_pipe2;
if (no_pipe2)
goto skip;
if (uv__pipe2(fds, flags | UV__O_CLOEXEC) == 0)
return 0;
if (errno != ENOSYS)
return -errno;
no_pipe2 = 1;
skip:
#endif
if (pipe(fds))
return -errno;
uv__cloexec(fds[0], 1);
uv__cloexec(fds[1], 1);
if (flags & UV__F_NONBLOCK) {
uv__nonblock(fds[0], 1);
uv__nonblock(fds[1], 1);
}
return 0;
}
/*
* Used for initializing stdio streams like options.stdin_stream. Returns
* zero on success. See also the cleanup section in uv_spawn().
*/
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
int mask;
int fd;
mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
switch (container->flags & mask) {
case UV_IGNORE:
return 0;
case UV_CREATE_PIPE:
assert(container->data.stream != NULL);
if (container->data.stream->type != UV_NAMED_PIPE)
return -EINVAL;
else
return uv__make_socketpair(fds, 0);
case UV_INHERIT_FD:
case UV_INHERIT_STREAM:
if (container->flags & UV_INHERIT_FD)
fd = container->data.fd;
else
fd = uv__stream_fd(container->data.stream);
if (fd == -1)
return -EINVAL;
fds[1] = fd;
return 0;
default:
assert(0 && "Unexpected flags");
return -EINVAL;
}
}
static int uv__process_open_stream(uv_stdio_container_t* container,
int pipefds[2],
int writable) {
int flags;
int err;
if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0)
return 0;
err = uv__close(pipefds[1]);
if (err != 0)
abort();
pipefds[1] = -1;
uv__nonblock(pipefds[0], 1);
if (container->data.stream->type == UV_NAMED_PIPE &&
((uv_pipe_t*)container->data.stream)->ipc)
flags = UV_STREAM_READABLE | UV_STREAM_WRITABLE;
else if (writable)
flags = UV_STREAM_WRITABLE;
else
flags = UV_STREAM_READABLE;
return uv__stream_open(container->data.stream, pipefds[0], flags);
}
static void uv__process_close_stream(uv_stdio_container_t* container) {
if (!(container->flags & UV_CREATE_PIPE)) return;
uv__stream_close((uv_stream_t*)container->data.stream);
}
static void uv__write_int(int fd, int val) {
ssize_t n;
do
n = write(fd, &val, sizeof(val));
while (n == -1 && errno == EINTR);
if (n == -1 && errno == EPIPE)
return; /* parent process has quit */
assert(n == sizeof(val));
}
#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be
* avoided. Since this isn't called on those targets, the function
* doesn't even need to be defined for them.
*/
static void uv__process_child_init(const uv_process_options_t* options,
int stdio_count,
int (*pipes)[2],
int error_fd) {
int close_fd;
int use_fd;
int fd;
if (options->flags & UV_PROCESS_DETACHED)
setsid();
/* First duplicate low numbered fds, since it's not safe to duplicate them,
* they could get replaced. Example: swapping stdout and stderr; without
* this fd 2 (stderr) would be duplicated into fd 1, thus making both
* stdout and stderr go to the same fd, which was not the intention. */
for (fd = 0; fd < stdio_count; fd++) {
use_fd = pipes[fd][1];
if (use_fd < 0 || use_fd >= fd)
continue;
pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count);
if (pipes[fd][1] == -1) {
uv__write_int(error_fd, -errno);
_exit(127);
}
}
for (fd = 0; fd < stdio_count; fd++) {
close_fd = pipes[fd][0];
use_fd = pipes[fd][1];
if (use_fd < 0) {
if (fd >= 3)
continue;
else {
/* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
* set
*/
use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR);
close_fd = use_fd;
if (use_fd == -1) {
uv__write_int(error_fd, -errno);
_exit(127);
}
}
}
if (fd == use_fd)
uv__cloexec_fcntl(use_fd, 0);
else
fd = dup2(use_fd, fd);
if (fd == -1) {
uv__write_int(error_fd, -errno);
_exit(127);
}
if (fd <= 2)
uv__nonblock_fcntl(fd, 0);
if (close_fd >= stdio_count)
uv__close(close_fd);
}
for (fd = 0; fd < stdio_count; fd++) {
use_fd = pipes[fd][1];
if (use_fd >= stdio_count)
uv__close(use_fd);
}
if (options->cwd != NULL && chdir(options->cwd)) {
uv__write_int(error_fd, -errno);
_exit(127);
}
if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
/* When dropping privileges from root, the `setgroups` call will
* remove any extraneous groups. If we don't call this, then
* even though our uid has dropped, we may still have groups
* that enable us to do super-user things. This will fail if we
* aren't root, so don't bother checking the return value, this
* is just done as an optimistic privilege dropping function.
*/
SAVE_ERRNO(setgroups(0, NULL));
}
if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) {
uv__write_int(error_fd, -errno);
_exit(127);
}
if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) {
uv__write_int(error_fd, -errno);
_exit(127);
}
if (options->env != NULL) {
environ = options->env;
}
execvp(options->file, options->args);
uv__write_int(error_fd, -errno);
_exit(127);
}
#endif
int uv_spawn(uv_loop_t* loop,
uv_process_t* process,
const uv_process_options_t* options) {
#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)
/* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */
return -ENOSYS;
#else
int signal_pipe[2] = { -1, -1 };
int (*pipes)[2];
int stdio_count;
ssize_t r;
pid_t pid;
int err;
int exec_errorno;
int i;
int status;
assert(options->file != NULL);
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
UV_PROCESS_SETGID |
UV_PROCESS_SETUID |
UV_PROCESS_WINDOWS_HIDE |
UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
QUEUE_INIT(&process->queue);
stdio_count = options->stdio_count;
if (stdio_count < 3)
stdio_count = 3;
err = -ENOMEM;
pipes = uv__malloc(stdio_count * sizeof(*pipes));
if (pipes == NULL)
goto error;
for (i = 0; i < stdio_count; i++) {
pipes[i][0] = -1;
pipes[i][1] = -1;
}
for (i = 0; i < options->stdio_count; i++) {
err = uv__process_init_stdio(options->stdio + i, pipes[i]);
if (err)
goto error;
}
/* This pipe is used by the parent to wait until
* the child has called `execve()`. We need this
* to avoid the following race condition:
*
* if ((pid = fork()) > 0) {
* kill(pid, SIGTERM);
* }
* else if (pid == 0) {
* execve("/bin/cat", argp, envp);
* }
*
* The parent sends a signal immediately after forking.
* Since the child may not have called `execve()` yet,
* there is no telling what process receives the signal,
* our fork or /bin/cat.
*
* To avoid ambiguity, we create a pipe with both ends
* marked close-on-exec. Then, after the call to `fork()`,
* the parent polls the read end until it EOFs or errors with EPIPE.
*/
err = uv__make_pipe(signal_pipe, 0);
if (err)
goto error;
uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
/* Acquire write lock to prevent opening new fds in worker threads */
uv_rwlock_wrlock(&loop->cloexec_lock);
pid = fork();
if (pid == -1) {
err = -errno;
uv_rwlock_wrunlock(&loop->cloexec_lock);
uv__close(signal_pipe[0]);
uv__close(signal_pipe[1]);
goto error;
}
if (pid == 0) {
uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
abort();
}
/* Release lock in parent process */
uv_rwlock_wrunlock(&loop->cloexec_lock);
uv__close(signal_pipe[1]);
process->status = 0;
exec_errorno = 0;
do
r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
while (r == -1 && errno == EINTR);
if (r == 0)
; /* okay, EOF */
else if (r == sizeof(exec_errorno)) {
do
err = waitpid(pid, &status, 0); /* okay, read errorno */
while (err == -1 && errno == EINTR);
assert(err == pid);
} else if (r == -1 && errno == EPIPE) {
do
err = waitpid(pid, &status, 0); /* okay, got EPIPE */
while (err == -1 && errno == EINTR);
assert(err == pid);
} else
abort();
uv__close_nocheckstdio(signal_pipe[0]);
for (i = 0; i < options->stdio_count; i++) {
err = uv__process_open_stream(options->stdio + i, pipes[i], i == 0);
if (err == 0)
continue;
while (i--)
uv__process_close_stream(options->stdio + i);
goto error;
}
/* Only activate this handle if exec() happened successfully */
if (exec_errorno == 0) {
QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
uv__handle_start(process);
}
process->pid = pid;
process->exit_cb = options->exit_cb;
uv__free(pipes);
return exec_errorno;
error:
if (pipes != NULL) {
for (i = 0; i < stdio_count; i++) {
if (i < options->stdio_count)
if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM))
continue;
if (pipes[i][0] != -1)
uv__close_nocheckstdio(pipes[i][0]);
if (pipes[i][1] != -1)
uv__close_nocheckstdio(pipes[i][1]);
}
uv__free(pipes);
}
return err;
#endif
}
int uv_process_kill(uv_process_t* process, int signum) {
return uv_kill(process->pid, signum);
}
int uv_kill(int pid, int signum) {
if (kill(pid, signum))
return -errno;
else
return 0;
}
void uv__process_close(uv_process_t* handle) {
QUEUE_REMOVE(&handle->queue);
uv__handle_stop(handle);
if (QUEUE_EMPTY(&handle->loop->process_handles))
uv_signal_stop(&handle->loop->child_watcher);
}
@@ -0,0 +1,45 @@
/* Copyright libuv project 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 "internal.h"
#include <stddef.h>
#include <unistd.h>
int uv_exepath(char* buffer, size_t* size) {
ssize_t n;
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
n = *size - 1;
if (n > 0)
n = readlink("/proc/self/exe", buffer, n);
if (n == -1)
return -errno;
buffer[n] = '\0';
*size = n;
return 0;
}
@@ -0,0 +1,113 @@
/* 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 "internal.h"
#include <stdlib.h>
#include <string.h>
extern void uv__set_process_title(const char* title);
static void* args_mem;
static struct {
char* str;
size_t len;
} process_title;
char** uv_setup_args(int argc, char** argv) {
char** new_argv;
size_t size;
char* s;
int i;
if (argc <= 0)
return argv;
/* Calculate how much memory we need for the argv strings. */
size = 0;
for (i = 0; i < argc; i++)
size += strlen(argv[i]) + 1;
#if defined(__MVS__)
/* argv is not adjacent. So just use argv[0] */
process_title.str = argv[0];
process_title.len = strlen(argv[0]);
#else
process_title.str = argv[0];
process_title.len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0];
assert(process_title.len + 1 == size); /* argv memory should be adjacent. */
#endif
/* Add space for the argv pointers. */
size += (argc + 1) * sizeof(char*);
new_argv = uv__malloc(size);
if (new_argv == NULL)
return argv;
args_mem = new_argv;
/* Copy over the strings and set up the pointer table. */
s = (char*) &new_argv[argc + 1];
for (i = 0; i < argc; i++) {
size = strlen(argv[i]) + 1;
memcpy(s, argv[i], size);
new_argv[i] = s;
s += size;
}
new_argv[i] = NULL;
return new_argv;
}
int uv_set_process_title(const char* title) {
if (process_title.len == 0)
return 0;
/* No need to terminate, byte after is always '\0'. */
strncpy(process_title.str, title, process_title.len);
uv__set_process_title(title);
return 0;
}
int uv_get_process_title(char* buffer, size_t size) {
if (buffer == NULL || size == 0)
return -EINVAL;
else if (size <= process_title.len)
return -ENOBUFS;
if (process_title.len != 0)
memcpy(buffer, process_title.str, process_title.len + 1);
buffer[process_title.len] = '\0';
return 0;
}
UV_DESTRUCTOR(static void free_args_mem(void)) {
uv__free(args_mem); /* Keep valgrind happy. */
args_mem = NULL;
}
@@ -0,0 +1,121 @@
/*
Copyright (c) 2016, Kari Tristan Helgason <kthelgason@gmail.com>
Permission to use, copy, modify, and/or 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.
*/
#include "uv-common.h"
#include "pthread-barrier.h"
#include <stdlib.h>
#include <assert.h>
/* TODO: support barrier_attr */
int pthread_barrier_init(pthread_barrier_t* barrier,
const void* barrier_attr,
unsigned count) {
int rc;
_uv_barrier* b;
if (barrier == NULL || count == 0)
return EINVAL;
if (barrier_attr != NULL)
return ENOTSUP;
b = uv__malloc(sizeof(*b));
if (b == NULL)
return ENOMEM;
b->in = 0;
b->out = 0;
b->threshold = count;
if ((rc = pthread_mutex_init(&b->mutex, NULL)) != 0)
goto error2;
if ((rc = pthread_cond_init(&b->cond, NULL)) != 0)
goto error;
barrier->b = b;
return 0;
error:
pthread_mutex_destroy(&b->mutex);
error2:
uv__free(b);
return rc;
}
int pthread_barrier_wait(pthread_barrier_t* barrier) {
int rc;
_uv_barrier* b;
if (barrier == NULL || barrier->b == NULL)
return EINVAL;
b = barrier->b;
/* Lock the mutex*/
if ((rc = pthread_mutex_lock(&b->mutex)) != 0)
return rc;
/* Increment the count. If this is the first thread to reach the threshold,
wake up waiters, unlock the mutex, then return
PTHREAD_BARRIER_SERIAL_THREAD. */
if (++b->in == b->threshold) {
b->in = 0;
b->out = b->threshold - 1;
rc = pthread_cond_signal(&b->cond);
assert(rc == 0);
pthread_mutex_unlock(&b->mutex);
return PTHREAD_BARRIER_SERIAL_THREAD;
}
/* Otherwise, wait for other threads until in is set to 0,
then return 0 to indicate this is not the first thread. */
do {
if ((rc = pthread_cond_wait(&b->cond, &b->mutex)) != 0)
break;
} while (b->in != 0);
/* mark thread exit */
b->out--;
pthread_cond_signal(&b->cond);
pthread_mutex_unlock(&b->mutex);
return rc;
}
int pthread_barrier_destroy(pthread_barrier_t* barrier) {
int rc;
_uv_barrier* b;
if (barrier == NULL || barrier->b == NULL)
return EINVAL;
b = barrier->b;
if ((rc = pthread_mutex_lock(&b->mutex)) != 0)
return rc;
if (b->in > 0 || b->out > 0)
rc = EBUSY;
pthread_mutex_unlock(&b->mutex);
if (rc)
return rc;
pthread_cond_destroy(&b->cond);
pthread_mutex_destroy(&b->mutex);
uv__free(barrier->b);
barrier->b = NULL;
return 0;
}
@@ -0,0 +1,56 @@
/* Copyright (c) 2013, Sony Mobile Communications AB
* Copyright (c) 2012, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Google Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT
OWNER 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.
*/
/* Android versions < 4.1 have a broken pthread_sigmask. */
#include <errno.h>
#include <pthread.h>
#include <signal.h>
int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) {
static int workaround;
int err;
if (workaround) {
return sigprocmask(how, set, oset);
} else {
err = pthread_sigmask(how, set, oset);
if (err) {
if (err == EINVAL && sigprocmask(how, set, oset) == 0) {
workaround = 1;
return 0;
} else {
return -1;
}
}
}
return 0;
}
@@ -0,0 +1,559 @@
/* 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 "internal.h"
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct {
uv_signal_t* handle;
int signum;
} uv__signal_msg_t;
RB_HEAD(uv__signal_tree_s, uv_signal_s);
static int uv__signal_unlock(void);
static int uv__signal_start(uv_signal_t* handle,
uv_signal_cb signal_cb,
int signum,
int oneshot);
static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2);
static void uv__signal_stop(uv_signal_t* handle);
static void uv__signal_unregister_handler(int signum);
static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT;
static struct uv__signal_tree_s uv__signal_tree =
RB_INITIALIZER(uv__signal_tree);
static int uv__signal_lock_pipefd[2];
RB_GENERATE_STATIC(uv__signal_tree_s,
uv_signal_s, tree_entry,
uv__signal_compare)
static void uv__signal_global_reinit(void);
static void uv__signal_global_init(void) {
if (!uv__signal_lock_pipefd[0])
/* pthread_atfork can register before and after handlers, one
* for each child. This only registers one for the child. That
* state is both persistent and cumulative, so if we keep doing
* it the handler functions will be called multiple times. Thus
* we only want to do it once.
*/
if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit))
abort();
if (uv__make_pipe(uv__signal_lock_pipefd, 0))
abort();
if (uv__signal_unlock())
abort();
}
static void uv__signal_global_reinit(void) {
/* We can only use signal-safe functions here.
* That includes read/write and close, fortunately.
* We do all of this directly here instead of resetting
* uv__signal_global_init_guard because
* uv__signal_global_once_init is only called from uv_loop_init
* and this needs to function in existing loops.
*/
uv__close(uv__signal_lock_pipefd[0]);
uv__signal_lock_pipefd[0] = -1;
uv__close(uv__signal_lock_pipefd[1]);
uv__signal_lock_pipefd[1] = -1;
uv__signal_global_init();
}
void uv__signal_global_once_init(void) {
uv_once(&uv__signal_global_init_guard, uv__signal_global_init);
}
static int uv__signal_lock(void) {
int r;
char data;
do {
r = read(uv__signal_lock_pipefd[0], &data, sizeof data);
} while (r < 0 && errno == EINTR);
return (r < 0) ? -1 : 0;
}
static int uv__signal_unlock(void) {
int r;
char data = 42;
do {
r = write(uv__signal_lock_pipefd[1], &data, sizeof data);
} while (r < 0 && errno == EINTR);
return (r < 0) ? -1 : 0;
}
static void uv__signal_block_and_lock(sigset_t* saved_sigmask) {
sigset_t new_mask;
if (sigfillset(&new_mask))
abort();
if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask))
abort();
if (uv__signal_lock())
abort();
}
static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) {
if (uv__signal_unlock())
abort();
if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL))
abort();
}
static uv_signal_t* uv__signal_first_handle(int signum) {
/* This function must be called with the signal lock held. */
uv_signal_t lookup;
uv_signal_t* handle;
lookup.signum = signum;
lookup.flags = 0;
lookup.loop = NULL;
handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup);
if (handle != NULL && handle->signum == signum)
return handle;
return NULL;
}
static void uv__signal_handler(int signum) {
uv__signal_msg_t msg;
uv_signal_t* handle;
int saved_errno;
saved_errno = errno;
memset(&msg, 0, sizeof msg);
if (uv__signal_lock()) {
errno = saved_errno;
return;
}
for (handle = uv__signal_first_handle(signum);
handle != NULL && handle->signum == signum;
handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
int r;
msg.signum = signum;
msg.handle = handle;
/* write() should be atomic for small data chunks, so the entire message
* should be written at once. In theory the pipe could become full, in
* which case the user is out of luck.
*/
do {
r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg);
} while (r == -1 && errno == EINTR);
assert(r == sizeof msg ||
(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)));
if (r != -1)
handle->caught_signals++;
}
uv__signal_unlock();
errno = saved_errno;
}
static int uv__signal_register_handler(int signum, int oneshot) {
/* When this function is called, the signal lock must be held. */
struct sigaction sa;
/* XXX use a separate signal stack? */
memset(&sa, 0, sizeof(sa));
if (sigfillset(&sa.sa_mask))
abort();
sa.sa_handler = uv__signal_handler;
sa.sa_flags = oneshot ? SA_RESETHAND : 0;
/* XXX save old action so we can restore it later on? */
if (sigaction(signum, &sa, NULL))
return -errno;
return 0;
}
static void uv__signal_unregister_handler(int signum) {
/* When this function is called, the signal lock must be held. */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
/* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a
* signal implies that it was successfully registered earlier, so EINVAL
* should never happen.
*/
if (sigaction(signum, &sa, NULL))
abort();
}
static int uv__signal_loop_once_init(uv_loop_t* loop) {
int err;
/* Return if already initialized. */
if (loop->signal_pipefd[0] != -1)
return 0;
err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK);
if (err)
return err;
uv__io_init(&loop->signal_io_watcher,
uv__signal_event,
loop->signal_pipefd[0]);
uv__io_start(loop, &loop->signal_io_watcher, POLLIN);
return 0;
}
int uv__signal_loop_fork(uv_loop_t* loop) {
uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);
uv__close(loop->signal_pipefd[0]);
uv__close(loop->signal_pipefd[1]);
loop->signal_pipefd[0] = -1;
loop->signal_pipefd[1] = -1;
return uv__signal_loop_once_init(loop);
}
void uv__signal_loop_cleanup(uv_loop_t* loop) {
QUEUE* q;
/* Stop all the signal watchers that are still attached to this loop. This
* ensures that the (shared) signal tree doesn't contain any invalid entries
* entries, and that signal handlers are removed when appropriate.
* It's safe to use QUEUE_FOREACH here because the handles and the handle
* queue are not modified by uv__signal_stop().
*/
QUEUE_FOREACH(q, &loop->handle_queue) {
uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue);
if (handle->type == UV_SIGNAL)
uv__signal_stop((uv_signal_t*) handle);
}
if (loop->signal_pipefd[0] != -1) {
uv__close(loop->signal_pipefd[0]);
loop->signal_pipefd[0] = -1;
}
if (loop->signal_pipefd[1] != -1) {
uv__close(loop->signal_pipefd[1]);
loop->signal_pipefd[1] = -1;
}
}
int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
int err;
err = uv__signal_loop_once_init(loop);
if (err)
return err;
uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
handle->signum = 0;
handle->caught_signals = 0;
handle->dispatched_signals = 0;
return 0;
}
void uv__signal_close(uv_signal_t* handle) {
uv__signal_stop(handle);
/* If there are any caught signals "trapped" in the signal pipe, we can't
* call the close callback yet. Otherwise, add the handle to the finish_close
* queue.
*/
if (handle->caught_signals == handle->dispatched_signals) {
uv__make_close_pending((uv_handle_t*) handle);
}
}
int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
return uv__signal_start(handle, signal_cb, signum, 0);
}
int uv_signal_start_oneshot(uv_signal_t* handle,
uv_signal_cb signal_cb,
int signum) {
return uv__signal_start(handle, signal_cb, signum, 1);
}
static int uv__signal_start(uv_signal_t* handle,
uv_signal_cb signal_cb,
int signum,
int oneshot) {
sigset_t saved_sigmask;
int err;
uv_signal_t* first_handle;
assert(!uv__is_closing(handle));
/* If the user supplies signum == 0, then return an error already. If the
* signum is otherwise invalid then uv__signal_register will find out
* eventually.
*/
if (signum == 0)
return -EINVAL;
/* Short circuit: if the signal watcher is already watching {signum} don't
* go through the process of deregistering and registering the handler.
* Additionally, this avoids pending signals getting lost in the small time
* time frame that handle->signum == 0.
*/
if (signum == handle->signum) {
handle->signal_cb = signal_cb;
return 0;
}
/* If the signal handler was already active, stop it first. */
if (handle->signum != 0) {
uv__signal_stop(handle);
}
uv__signal_block_and_lock(&saved_sigmask);
/* If at this point there are no active signal watchers for this signum (in
* any of the loops), it's time to try and register a handler for it here.
* Also in case there's only one-shot handlers and a regular handler comes in.
*/
first_handle = uv__signal_first_handle(signum);
if (first_handle == NULL ||
(!oneshot && (first_handle->flags & UV__SIGNAL_ONE_SHOT))) {
err = uv__signal_register_handler(signum, oneshot);
if (err) {
/* Registering the signal handler failed. Must be an invalid signal. */
uv__signal_unlock_and_unblock(&saved_sigmask);
return err;
}
}
handle->signum = signum;
if (oneshot)
handle->flags |= UV__SIGNAL_ONE_SHOT;
RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle);
uv__signal_unlock_and_unblock(&saved_sigmask);
handle->signal_cb = signal_cb;
uv__handle_start(handle);
return 0;
}
static void uv__signal_event(uv_loop_t* loop,
uv__io_t* w,
unsigned int events) {
uv__signal_msg_t* msg;
uv_signal_t* handle;
char buf[sizeof(uv__signal_msg_t) * 32];
size_t bytes, end, i;
int r;
bytes = 0;
end = 0;
do {
r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes);
if (r == -1 && errno == EINTR)
continue;
if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
/* If there are bytes in the buffer already (which really is extremely
* unlikely if possible at all) we can't exit the function here. We'll
* spin until more bytes are read instead.
*/
if (bytes > 0)
continue;
/* Otherwise, there was nothing there. */
return;
}
/* Other errors really should never happen. */
if (r == -1)
abort();
bytes += r;
/* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */
end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t);
for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) {
msg = (uv__signal_msg_t*) (buf + i);
handle = msg->handle;
if (msg->signum == handle->signum) {
assert(!(handle->flags & UV_CLOSING));
handle->signal_cb(handle, handle->signum);
}
handle->dispatched_signals++;
if (handle->flags & UV__SIGNAL_ONE_SHOT)
uv__signal_stop(handle);
/* If uv_close was called while there were caught signals that were not
* yet dispatched, the uv__finish_close was deferred. Make close pending
* now if this has happened.
*/
if ((handle->flags & UV_CLOSING) &&
(handle->caught_signals == handle->dispatched_signals)) {
uv__make_close_pending((uv_handle_t*) handle);
}
}
bytes -= end;
/* If there are any "partial" messages left, move them to the start of the
* the buffer, and spin. This should not happen.
*/
if (bytes) {
memmove(buf, buf + end, bytes);
continue;
}
} while (end == sizeof buf);
}
static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
int f1;
int f2;
/* Compare signums first so all watchers with the same signnum end up
* adjacent.
*/
if (w1->signum < w2->signum) return -1;
if (w1->signum > w2->signum) return 1;
/* Handlers without UV__SIGNAL_ONE_SHOT set will come first, so if the first
* handler returned is a one-shot handler, the rest will be too.
*/
f1 = w1->flags & UV__SIGNAL_ONE_SHOT;
f2 = w2->flags & UV__SIGNAL_ONE_SHOT;
if (f1 < f2) return -1;
if (f1 > f2) return 1;
/* Sort by loop pointer, so we can easily look up the first item after
* { .signum = x, .loop = NULL }.
*/
if (w1->loop < w2->loop) return -1;
if (w1->loop > w2->loop) return 1;
if (w1 < w2) return -1;
if (w1 > w2) return 1;
return 0;
}
int uv_signal_stop(uv_signal_t* handle) {
assert(!uv__is_closing(handle));
uv__signal_stop(handle);
return 0;
}
static void uv__signal_stop(uv_signal_t* handle) {
uv_signal_t* removed_handle;
sigset_t saved_sigmask;
uv_signal_t* first_handle;
int rem_oneshot;
int first_oneshot;
int ret;
/* If the watcher wasn't started, this is a no-op. */
if (handle->signum == 0)
return;
uv__signal_block_and_lock(&saved_sigmask);
removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle);
assert(removed_handle == handle);
(void) removed_handle;
/* Check if there are other active signal watchers observing this signal. If
* not, unregister the signal handler.
*/
first_handle = uv__signal_first_handle(handle->signum);
if (first_handle == NULL) {
uv__signal_unregister_handler(handle->signum);
} else {
rem_oneshot = handle->flags & UV__SIGNAL_ONE_SHOT;
first_oneshot = first_handle->flags & UV__SIGNAL_ONE_SHOT;
if (first_oneshot && !rem_oneshot) {
ret = uv__signal_register_handler(handle->signum, 1);
assert(ret == 0);
}
}
uv__signal_unlock_and_unblock(&saved_sigmask);
handle->signum = 0;
uv__handle_stop(handle);
}
@@ -0,0 +1,53 @@
/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
*
* Permission to use, copy, modify, and/or 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.
*/
#ifndef UV_SPINLOCK_H_
#define UV_SPINLOCK_H_
#include "internal.h" /* ACCESS_ONCE, UV_UNUSED */
#include "atomic-ops.h"
#define UV_SPINLOCK_INITIALIZER { 0 }
typedef struct {
int lock;
} uv_spinlock_t;
UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock));
UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock));
UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock));
UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock));
UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) {
ACCESS_ONCE(int, spinlock->lock) = 0;
}
UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) {
while (!uv_spinlock_trylock(spinlock)) cpu_relax();
}
UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) {
ACCESS_ONCE(int, spinlock->lock) = 0;
}
UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) {
/* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing.
* Not really critical until we have locks that are (frequently) contended
* for by several threads.
*/
return 0 == cmpxchgi(&spinlock->lock, 0, 1);
}
#endif /* UV_SPINLOCK_H_ */
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,821 @@
/* 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 "internal.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#ifndef SUNOS_NO_IFADDRS
# include <ifaddrs.h>
#endif
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_arp.h>
#include <sys/sockio.h>
#include <sys/loadavg.h>
#include <sys/time.h>
#include <unistd.h>
#include <kstat.h>
#include <fcntl.h>
#include <sys/port.h>
#include <port.h>
#define PORT_FIRED 0x69
#define PORT_UNUSED 0x0
#define PORT_LOADED 0x99
#define PORT_DELETED -1
#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64)
#define PROCFS_FILE_OFFSET_BITS_HACK 1
#undef _FILE_OFFSET_BITS
#else
#define PROCFS_FILE_OFFSET_BITS_HACK 0
#endif
#include <procfs.h>
#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1)
#define _FILE_OFFSET_BITS 64
#endif
int uv__platform_loop_init(uv_loop_t* loop) {
int err;
int fd;
loop->fs_fd = -1;
loop->backend_fd = -1;
fd = port_create();
if (fd == -1)
return -errno;
err = uv__cloexec(fd, 1);
if (err) {
uv__close(fd);
return err;
}
loop->backend_fd = fd;
return 0;
}
void uv__platform_loop_delete(uv_loop_t* loop) {
if (loop->fs_fd != -1) {
uv__close(loop->fs_fd);
loop->fs_fd = -1;
}
if (loop->backend_fd != -1) {
uv__close(loop->backend_fd);
loop->backend_fd = -1;
}
}
int uv__io_fork(uv_loop_t* loop) {
#if defined(PORT_SOURCE_FILE)
if (loop->fs_fd != -1) {
/* stop the watcher before we blow away its fileno */
uv__io_stop(loop, &loop->fs_event_watcher, POLLIN);
}
#endif
uv__platform_loop_delete(loop);
return uv__platform_loop_init(loop);
}
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
struct port_event* events;
uintptr_t i;
uintptr_t nfds;
assert(loop->watchers != NULL);
events = (struct port_event*) loop->watchers[loop->nwatchers];
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
if (events == NULL)
return;
/* Invalidate events with same file descriptor */
for (i = 0; i < nfds; i++)
if ((int) events[i].portev_object == fd)
events[i].portev_object = -1;
}
int uv__io_check_fd(uv_loop_t* loop, int fd) {
if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0))
return -errno;
if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd))
abort();
return 0;
}
void uv__io_poll(uv_loop_t* loop, int timeout) {
struct port_event events[1024];
struct port_event* pe;
struct timespec spec;
QUEUE* q;
uv__io_t* w;
sigset_t* pset;
sigset_t set;
uint64_t base;
uint64_t diff;
unsigned int nfds;
unsigned int i;
int saved_errno;
int have_signals;
int nevents;
int count;
int err;
int fd;
if (loop->nfds == 0) {
assert(QUEUE_EMPTY(&loop->watcher_queue));
return;
}
while (!QUEUE_EMPTY(&loop->watcher_queue)) {
q = QUEUE_HEAD(&loop->watcher_queue);
QUEUE_REMOVE(q);
QUEUE_INIT(q);
w = QUEUE_DATA(q, uv__io_t, watcher_queue);
assert(w->pevents != 0);
if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0))
abort();
w->events = w->pevents;
}
pset = NULL;
if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
pset = &set;
sigemptyset(pset);
sigaddset(pset, SIGPROF);
}
assert(timeout >= -1);
base = loop->time;
count = 48; /* Benchmarks suggest this gives the best throughput. */
for (;;) {
if (timeout != -1) {
spec.tv_sec = timeout / 1000;
spec.tv_nsec = (timeout % 1000) * 1000000;
}
/* Work around a kernel bug where nfds is not updated. */
events[0].portev_source = 0;
nfds = 1;
saved_errno = 0;
if (pset != NULL)
pthread_sigmask(SIG_BLOCK, pset, NULL);
err = port_getn(loop->backend_fd,
events,
ARRAY_SIZE(events),
&nfds,
timeout == -1 ? NULL : &spec);
if (pset != NULL)
pthread_sigmask(SIG_UNBLOCK, pset, NULL);
if (err) {
/* Work around another kernel bug: port_getn() may return events even
* on error.
*/
if (errno == EINTR || errno == ETIME)
saved_errno = errno;
else
abort();
}
/* Update loop->time unconditionally. It's tempting to skip the update when
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
* operating system didn't reschedule our process while in the syscall.
*/
SAVE_ERRNO(uv__update_time(loop));
if (events[0].portev_source == 0) {
if (timeout == 0)
return;
if (timeout == -1)
continue;
goto update_timeout;
}
if (nfds == 0) {
assert(timeout != -1);
return;
}
have_signals = 0;
nevents = 0;
assert(loop->watchers != NULL);
loop->watchers[loop->nwatchers] = (void*) events;
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
for (i = 0; i < nfds; i++) {
pe = events + i;
fd = pe->portev_object;
/* Skip invalidated events, see uv__platform_invalidate_fd */
if (fd == -1)
continue;
assert(fd >= 0);
assert((unsigned) fd < loop->nwatchers);
w = loop->watchers[fd];
/* File descriptor that we've stopped watching, ignore. */
if (w == NULL)
continue;
/* Run signal watchers last. This also affects child process watchers
* because those are implemented in terms of signal watchers.
*/
if (w == &loop->signal_io_watcher)
have_signals = 1;
else
w->cb(loop, w, pe->portev_events);
nevents++;
if (w != loop->watchers[fd])
continue; /* Disabled by callback. */
/* Events Ports operates in oneshot mode, rearm timer on next run. */
if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue))
QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
}
if (have_signals != 0)
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
loop->watchers[loop->nwatchers] = NULL;
loop->watchers[loop->nwatchers + 1] = NULL;
if (have_signals != 0)
return; /* Event loop should cycle now so don't poll again. */
if (nevents != 0) {
if (nfds == ARRAY_SIZE(events) && --count != 0) {
/* Poll for more events but don't block this time. */
timeout = 0;
continue;
}
return;
}
if (saved_errno == ETIME) {
assert(timeout != -1);
return;
}
if (timeout == 0)
return;
if (timeout == -1)
continue;
update_timeout:
assert(timeout > 0);
diff = loop->time - base;
if (diff >= (uint64_t) timeout)
return;
timeout -= diff;
}
}
uint64_t uv__hrtime(uv_clocktype_t type) {
return gethrtime();
}
/*
* We could use a static buffer for the path manipulations that we need outside
* of the function, but this function could be called by multiple consumers and
* we don't want to potentially create a race condition in the use of snprintf.
*/
int uv_exepath(char* buffer, size_t* size) {
ssize_t res;
char buf[128];
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
snprintf(buf, sizeof(buf), "/proc/%lu/path/a.out", (unsigned long) getpid());
res = *size - 1;
if (res > 0)
res = readlink(buf, buffer, res);
if (res == -1)
return -errno;
buffer[res] = '\0';
*size = res;
return 0;
}
uint64_t uv_get_free_memory(void) {
return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES);
}
uint64_t uv_get_total_memory(void) {
return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
}
void uv_loadavg(double avg[3]) {
(void) getloadavg(avg, 3);
}
#if defined(PORT_SOURCE_FILE)
static int uv__fs_event_rearm(uv_fs_event_t *handle) {
if (handle->fd == -1)
return -EBADF;
if (port_associate(handle->loop->fs_fd,
PORT_SOURCE_FILE,
(uintptr_t) &handle->fo,
FILE_ATTRIB | FILE_MODIFIED,
handle) == -1) {
return -errno;
}
handle->fd = PORT_LOADED;
return 0;
}
static void uv__fs_event_read(uv_loop_t* loop,
uv__io_t* w,
unsigned int revents) {
uv_fs_event_t *handle = NULL;
timespec_t timeout;
port_event_t pe;
int events;
int r;
(void) w;
(void) revents;
do {
uint_t n = 1;
/*
* Note that our use of port_getn() here (and not port_get()) is deliberate:
* there is a bug in event ports (Sun bug 6456558) whereby a zeroed timeout
* causes port_get() to return success instead of ETIME when there aren't
* actually any events (!); by using port_getn() in lieu of port_get(),
* we can at least workaround the bug by checking for zero returned events
* and treating it as we would ETIME.
*/
do {
memset(&timeout, 0, sizeof timeout);
r = port_getn(loop->fs_fd, &pe, 1, &n, &timeout);
}
while (r == -1 && errno == EINTR);
if ((r == -1 && errno == ETIME) || n == 0)
break;
handle = (uv_fs_event_t*) pe.portev_user;
assert((r == 0) && "unexpected port_get() error");
events = 0;
if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED))
events |= UV_CHANGE;
if (pe.portev_events & ~(FILE_ATTRIB | FILE_MODIFIED))
events |= UV_RENAME;
assert(events != 0);
handle->fd = PORT_FIRED;
handle->cb(handle, NULL, events, 0);
if (handle->fd != PORT_DELETED) {
r = uv__fs_event_rearm(handle);
if (r != 0)
handle->cb(handle, NULL, 0, r);
}
}
while (handle->fd != PORT_DELETED);
}
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* path,
unsigned int flags) {
int portfd;
int first_run;
int err;
if (uv__is_active(handle))
return -EINVAL;
first_run = 0;
if (handle->loop->fs_fd == -1) {
portfd = port_create();
if (portfd == -1)
return -errno;
handle->loop->fs_fd = portfd;
first_run = 1;
}
uv__handle_start(handle);
handle->path = uv__strdup(path);
handle->fd = PORT_UNUSED;
handle->cb = cb;
memset(&handle->fo, 0, sizeof handle->fo);
handle->fo.fo_name = handle->path;
err = uv__fs_event_rearm(handle);
if (err != 0) {
uv_fs_event_stop(handle);
return err;
}
if (first_run) {
uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd);
uv__io_start(handle->loop, &handle->loop->fs_event_watcher, POLLIN);
}
return 0;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
if (!uv__is_active(handle))
return 0;
if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) {
port_dissociate(handle->loop->fs_fd,
PORT_SOURCE_FILE,
(uintptr_t) &handle->fo);
}
handle->fd = PORT_DELETED;
uv__free(handle->path);
handle->path = NULL;
handle->fo.fo_name = NULL;
uv__handle_stop(handle);
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}
#else /* !defined(PORT_SOURCE_FILE) */
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
return -ENOSYS;
}
int uv_fs_event_start(uv_fs_event_t* handle,
uv_fs_event_cb cb,
const char* filename,
unsigned int flags) {
return -ENOSYS;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
return -ENOSYS;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
UNREACHABLE();
}
#endif /* defined(PORT_SOURCE_FILE) */
int uv_resident_set_memory(size_t* rss) {
psinfo_t psinfo;
int err;
int fd;
fd = open("/proc/self/psinfo", O_RDONLY);
if (fd == -1)
return -errno;
/* FIXME(bnoordhuis) Handle EINTR. */
err = -EINVAL;
if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) {
*rss = (size_t)psinfo.pr_rssize * 1024;
err = 0;
}
uv__close(fd);
return err;
}
int uv_uptime(double* uptime) {
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
long hz = sysconf(_SC_CLK_TCK);
kc = kstat_open();
if (kc == NULL)
return -EPERM;
ksp = kstat_lookup(kc, (char*) "unix", 0, (char*) "system_misc");
if (kstat_read(kc, ksp, NULL) == -1) {
*uptime = -1;
} else {
knp = (kstat_named_t*) kstat_data_lookup(ksp, (char*) "clk_intr");
*uptime = knp->value.ul / hz;
}
kstat_close(kc);
return 0;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
int lookup_instance;
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
uv_cpu_info_t* cpu_info;
kc = kstat_open();
if (kc == NULL)
return -EPERM;
/* Get count of cpus */
lookup_instance = 0;
while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) {
lookup_instance++;
}
*cpu_infos = uv__malloc(lookup_instance * sizeof(**cpu_infos));
if (!(*cpu_infos)) {
kstat_close(kc);
return -ENOMEM;
}
*count = lookup_instance;
cpu_info = *cpu_infos;
lookup_instance = 0;
while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) {
if (kstat_read(kc, ksp, NULL) == -1) {
cpu_info->speed = 0;
cpu_info->model = NULL;
} else {
knp = kstat_data_lookup(ksp, (char*) "clock_MHz");
assert(knp->data_type == KSTAT_DATA_INT32 ||
knp->data_type == KSTAT_DATA_INT64);
cpu_info->speed = (knp->data_type == KSTAT_DATA_INT32) ? knp->value.i32
: knp->value.i64;
knp = kstat_data_lookup(ksp, (char*) "brand");
assert(knp->data_type == KSTAT_DATA_STRING);
cpu_info->model = uv__strdup(KSTAT_NAMED_STR_PTR(knp));
}
lookup_instance++;
cpu_info++;
}
cpu_info = *cpu_infos;
lookup_instance = 0;
for (;;) {
ksp = kstat_lookup(kc, (char*) "cpu", lookup_instance, (char*) "sys");
if (ksp == NULL)
break;
if (kstat_read(kc, ksp, NULL) == -1) {
cpu_info->cpu_times.user = 0;
cpu_info->cpu_times.nice = 0;
cpu_info->cpu_times.sys = 0;
cpu_info->cpu_times.idle = 0;
cpu_info->cpu_times.irq = 0;
} else {
knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_user");
assert(knp->data_type == KSTAT_DATA_UINT64);
cpu_info->cpu_times.user = knp->value.ui64;
knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_kernel");
assert(knp->data_type == KSTAT_DATA_UINT64);
cpu_info->cpu_times.sys = knp->value.ui64;
knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_idle");
assert(knp->data_type == KSTAT_DATA_UINT64);
cpu_info->cpu_times.idle = knp->value.ui64;
knp = kstat_data_lookup(ksp, (char*) "intr");
assert(knp->data_type == KSTAT_DATA_UINT64);
cpu_info->cpu_times.irq = knp->value.ui64;
cpu_info->cpu_times.nice = 0;
}
lookup_instance++;
cpu_info++;
}
kstat_close(kc);
return 0;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(cpu_infos[i].model);
}
uv__free(cpu_infos);
}
#ifdef SUNOS_NO_IFADDRS
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
return -ENOSYS;
}
#else /* SUNOS_NO_IFADDRS */
/*
* Inspired By:
* https://blogs.oracle.com/paulie/entry/retrieving_mac_address_in_solaris
* http://www.pauliesworld.org/project/getmac.c
*/
static int uv__set_phys_addr(uv_interface_address_t* address,
struct ifaddrs* ent) {
struct sockaddr_dl* sa_addr;
int sockfd;
int i;
struct arpreq arpreq;
/* This appears to only work as root */
sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
for (i = 0; i < sizeof(address->phys_addr); i++) {
if (address->phys_addr[i] != 0)
return 0;
}
memset(&arpreq, 0, sizeof(arpreq));
if (address->address.address4.sin_family == AF_INET) {
struct sockaddr_in* sin = ((struct sockaddr_in*)&arpreq.arp_pa);
sin->sin_addr.s_addr = address->address.address4.sin_addr.s_addr;
} else if (address->address.address4.sin_family == AF_INET6) {
struct sockaddr_in6* sin = ((struct sockaddr_in6*)&arpreq.arp_pa);
memcpy(sin->sin6_addr.s6_addr,
address->address.address6.sin6_addr.s6_addr,
sizeof(address->address.address6.sin6_addr.s6_addr));
} else {
return 0;
}
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
return -errno;
if (ioctl(sockfd, SIOCGARP, (char*)&arpreq) == -1) {
uv__close(sockfd);
return -errno;
}
memcpy(address->phys_addr, arpreq.arp_ha.sa_data, sizeof(address->phys_addr));
uv__close(sockfd);
return 0;
}
static int uv__ifaddr_exclude(struct ifaddrs *ent) {
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
return 1;
if (ent->ifa_addr == NULL)
return 1;
if (ent->ifa_addr->sa_family == PF_PACKET)
return 1;
return 0;
}
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
uv_interface_address_t* address;
struct ifaddrs* addrs;
struct ifaddrs* ent;
int i;
if (getifaddrs(&addrs))
return -errno;
*count = 0;
/* Count the number of interfaces */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent))
continue;
(*count)++;
}
*addresses = uv__malloc(*count * sizeof(**addresses));
if (!(*addresses)) {
freeifaddrs(addrs);
return -ENOMEM;
}
address = *addresses;
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent))
continue;
address->name = uv__strdup(ent->ifa_name);
if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
} else {
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
}
if (ent->ifa_netmask->sa_family == AF_INET6) {
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
} else {
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
}
address->is_internal = !!((ent->ifa_flags & IFF_PRIVATE) ||
(ent->ifa_flags & IFF_LOOPBACK));
uv__set_phys_addr(address, ent);
address++;
}
freeifaddrs(addrs);
return 0;
}
#endif /* SUNOS_NO_IFADDRS */
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
int i;
for (i = 0; i < count; i++) {
uv__free(addresses[i].name);
}
uv__free(addresses);
}
@@ -0,0 +1,36 @@
/* Copyright libuv project 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 "internal.h"
#include <stdint.h>
#include <sys/sysinfo.h>
void uv_loadavg(double avg[3]) {
struct sysinfo info;
if (sysinfo(&info) < 0) return;
avg[0] = (double) info.loads[0] / 65536.0;
avg[1] = (double) info.loads[1] / 65536.0;
avg[2] = (double) info.loads[2] / 65536.0;
}
@@ -0,0 +1,42 @@
/* Copyright libuv project 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 "internal.h"
#include <stdint.h>
#include <sys/sysinfo.h>
uint64_t uv_get_free_memory(void) {
struct sysinfo info;
if (sysinfo(&info) == 0)
return (uint64_t) info.freeram * info.mem_unit;
return 0;
}
uint64_t uv_get_total_memory(void) {
struct sysinfo info;
if (sysinfo(&info) == 0)
return (uint64_t) info.totalram * info.mem_unit;
return 0;
}
+395
View File
@@ -0,0 +1,395 @@
/* 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 "internal.h"
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) {
int sockfd;
int err;
if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) {
handle->flags |= flags;
return 0;
}
err = uv__socket(domain, SOCK_STREAM, 0);
if (err < 0)
return err;
sockfd = err;
err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
if (err) {
uv__close(sockfd);
return err;
}
return 0;
}
int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
int domain;
/* Use the lower 8 bits for the domain */
domain = flags & 0xFF;
if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
return -EINVAL;
if (flags & ~0xFF)
return -EINVAL;
uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
/* If anything fails beyond this point we need to remove the handle from
* the handle queue, since it was added by uv__handle_init in uv_stream_init.
*/
if (domain != AF_UNSPEC) {
int err = maybe_new_socket(tcp, domain, 0);
if (err) {
QUEUE_REMOVE(&tcp->handle_queue);
return err;
}
}
return 0;
}
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
return uv_tcp_init_ex(loop, tcp, AF_UNSPEC);
}
int uv__tcp_bind(uv_tcp_t* tcp,
const struct sockaddr* addr,
unsigned int addrlen,
unsigned int flags) {
int err;
int on;
/* Cannot set IPv6-only mode on non-IPv6 socket. */
if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
return -EINVAL;
err = maybe_new_socket(tcp,
addr->sa_family,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
if (err)
return err;
on = 1;
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
return -errno;
#ifdef IPV6_V6ONLY
if (addr->sa_family == AF_INET6) {
on = (flags & UV_TCP_IPV6ONLY) != 0;
if (setsockopt(tcp->io_watcher.fd,
IPPROTO_IPV6,
IPV6_V6ONLY,
&on,
sizeof on) == -1) {
#if defined(__MVS__)
if (errno == EOPNOTSUPP)
return -EINVAL;
#endif
return -errno;
}
}
#endif
errno = 0;
if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) {
if (errno == EAFNOSUPPORT)
/* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
* socket created with AF_INET to an AF_INET6 address or vice versa. */
return -EINVAL;
return -errno;
}
tcp->delayed_error = -errno;
tcp->flags |= UV_HANDLE_BOUND;
if (addr->sa_family == AF_INET6)
tcp->flags |= UV_HANDLE_IPV6;
return 0;
}
int uv__tcp_connect(uv_connect_t* req,
uv_tcp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen,
uv_connect_cb cb) {
int err;
int r;
assert(handle->type == UV_TCP);
if (handle->connect_req != NULL)
return -EALREADY; /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */
err = maybe_new_socket(handle,
addr->sa_family,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
if (err)
return err;
handle->delayed_error = 0;
do {
errno = 0;
r = connect(uv__stream_fd(handle), addr, addrlen);
} while (r == -1 && errno == EINTR);
/* We not only check the return value, but also check the errno != 0.
* Because in rare cases connect() will return -1 but the errno
* is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227)
* and actually the tcp three-way handshake is completed.
*/
if (r == -1 && errno != 0) {
if (errno == EINPROGRESS)
; /* not an error */
else if (errno == ECONNREFUSED)
/* If we get a ECONNREFUSED wait until the next tick to report the
* error. Solaris wants to report immediately--other unixes want to
* wait.
*/
handle->delayed_error = -errno;
else
return -errno;
}
uv__req_init(handle->loop, req, UV_CONNECT);
req->cb = cb;
req->handle = (uv_stream_t*) handle;
QUEUE_INIT(&req->queue);
handle->connect_req = req;
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
if (handle->delayed_error)
uv__io_feed(handle->loop, &handle->io_watcher);
return 0;
}
int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
int err;
err = uv__nonblock(sock, 1);
if (err)
return err;
return uv__stream_open((uv_stream_t*)handle,
sock,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
}
int uv_tcp_getsockname(const uv_tcp_t* handle,
struct sockaddr* name,
int* namelen) {
socklen_t socklen;
if (handle->delayed_error)
return handle->delayed_error;
if (uv__stream_fd(handle) < 0)
return -EINVAL; /* FIXME(bnoordhuis) -EBADF */
/* sizeof(socklen_t) != sizeof(int) on some systems. */
socklen = (socklen_t) *namelen;
if (getsockname(uv__stream_fd(handle), name, &socklen))
return -errno;
*namelen = (int) socklen;
return 0;
}
int uv_tcp_getpeername(const uv_tcp_t* handle,
struct sockaddr* name,
int* namelen) {
socklen_t socklen;
if (handle->delayed_error)
return handle->delayed_error;
if (uv__stream_fd(handle) < 0)
return -EINVAL; /* FIXME(bnoordhuis) -EBADF */
/* sizeof(socklen_t) != sizeof(int) on some systems. */
socklen = (socklen_t) *namelen;
if (getpeername(uv__stream_fd(handle), name, &socklen))
return -errno;
*namelen = (int) socklen;
return 0;
}
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
static int single_accept = -1;
int err;
if (tcp->delayed_error)
return tcp->delayed_error;
if (single_accept == -1) {
const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */
}
if (single_accept)
tcp->flags |= UV_TCP_SINGLE_ACCEPT;
err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE);
if (err)
return err;
#ifdef __MVS__
/* on zOS the listen call does not bind automatically
if the socket is unbound. Hence the manual binding to
an arbitrary port is required to be done manually
*/
if (!(tcp->flags & UV_HANDLE_BOUND)) {
struct sockaddr_storage saddr;
socklen_t slen = sizeof(saddr);
memset(&saddr, 0, sizeof(saddr));
if (getsockname(tcp->io_watcher.fd, (struct sockaddr*) &saddr, &slen))
return -errno;
if (bind(tcp->io_watcher.fd, (struct sockaddr*) &saddr, slen))
return -errno;
tcp->flags |= UV_HANDLE_BOUND;
}
#endif
if (listen(tcp->io_watcher.fd, backlog))
return -errno;
tcp->connection_cb = cb;
tcp->flags |= UV_HANDLE_BOUND;
/* Start listening for connections. */
tcp->io_watcher.cb = uv__server_io;
uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
return 0;
}
int uv__tcp_nodelay(int fd, int on) {
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)))
return -errno;
return 0;
}
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
return -errno;
#ifdef TCP_KEEPIDLE
if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
return -errno;
#endif
/* Solaris/SmartOS, if you don't support keep-alive,
* then don't advertise it in your system headers...
*/
/* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
#if defined(TCP_KEEPALIVE) && !defined(__sun)
if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
return -errno;
#endif
return 0;
}
int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
int err;
if (uv__stream_fd(handle) != -1) {
err = uv__tcp_nodelay(uv__stream_fd(handle), on);
if (err)
return err;
}
if (on)
handle->flags |= UV_TCP_NODELAY;
else
handle->flags &= ~UV_TCP_NODELAY;
return 0;
}
int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
int err;
if (uv__stream_fd(handle) != -1) {
err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
if (err)
return err;
}
if (on)
handle->flags |= UV_TCP_KEEPALIVE;
else
handle->flags &= ~UV_TCP_KEEPALIVE;
/* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
* uv_tcp_t with an int that's almost never used...
*/
return 0;
}
int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
if (enable)
handle->flags &= ~UV_TCP_SINGLE_ACCEPT;
else
handle->flags |= UV_TCP_SINGLE_ACCEPT;
return 0;
}
void uv__tcp_close(uv_tcp_t* handle) {
uv__stream_close((uv_stream_t*)handle);
}
@@ -0,0 +1,575 @@
/* 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 "internal.h"
#include <pthread.h>
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h> /* getrlimit() */
#include <unistd.h> /* getpagesize() */
#include <limits.h>
#ifdef __MVS__
#include <sys/ipc.h>
#include <sys/sem.h>
#endif
#undef NANOSEC
#define NANOSEC ((uint64_t) 1e9)
int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
int err;
pthread_attr_t* attr;
#if defined(__APPLE__)
pthread_attr_t attr_storage;
struct rlimit lim;
#endif
/* On OSX threads other than the main thread are created with a reduced stack
* size by default, adjust it to RLIMIT_STACK.
*/
#if defined(__APPLE__)
if (getrlimit(RLIMIT_STACK, &lim))
abort();
attr = &attr_storage;
if (pthread_attr_init(attr))
abort();
if (lim.rlim_cur != RLIM_INFINITY) {
/* pthread_attr_setstacksize() expects page-aligned values. */
lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
if (lim.rlim_cur >= PTHREAD_STACK_MIN)
if (pthread_attr_setstacksize(attr, lim.rlim_cur))
abort();
}
#else
attr = NULL;
#endif
err = pthread_create(tid, attr, (void*(*)(void*)) entry, arg);
if (attr != NULL)
pthread_attr_destroy(attr);
return -err;
}
uv_thread_t uv_thread_self(void) {
return pthread_self();
}
int uv_thread_join(uv_thread_t *tid) {
return -pthread_join(*tid, NULL);
}
int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
return pthread_equal(*t1, *t2);
}
int uv_mutex_init(uv_mutex_t* mutex) {
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
return -pthread_mutex_init(mutex, NULL);
#else
pthread_mutexattr_t attr;
int err;
if (pthread_mutexattr_init(&attr))
abort();
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK))
abort();
err = pthread_mutex_init(mutex, &attr);
if (pthread_mutexattr_destroy(&attr))
abort();
return -err;
#endif
}
void uv_mutex_destroy(uv_mutex_t* mutex) {
if (pthread_mutex_destroy(mutex))
abort();
}
void uv_mutex_lock(uv_mutex_t* mutex) {
if (pthread_mutex_lock(mutex))
abort();
}
int uv_mutex_trylock(uv_mutex_t* mutex) {
int err;
err = pthread_mutex_trylock(mutex);
if (err) {
if (err != EBUSY && err != EAGAIN)
abort();
return -EBUSY;
}
return 0;
}
void uv_mutex_unlock(uv_mutex_t* mutex) {
if (pthread_mutex_unlock(mutex))
abort();
}
int uv_rwlock_init(uv_rwlock_t* rwlock) {
return -pthread_rwlock_init(rwlock, NULL);
}
void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
if (pthread_rwlock_destroy(rwlock))
abort();
}
void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
if (pthread_rwlock_rdlock(rwlock))
abort();
}
int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
int err;
err = pthread_rwlock_tryrdlock(rwlock);
if (err) {
if (err != EBUSY && err != EAGAIN)
abort();
return -EBUSY;
}
return 0;
}
void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
if (pthread_rwlock_unlock(rwlock))
abort();
}
void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
if (pthread_rwlock_wrlock(rwlock))
abort();
}
int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
int err;
err = pthread_rwlock_trywrlock(rwlock);
if (err) {
if (err != EBUSY && err != EAGAIN)
abort();
return -EBUSY;
}
return 0;
}
void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
if (pthread_rwlock_unlock(rwlock))
abort();
}
void uv_once(uv_once_t* guard, void (*callback)(void)) {
if (pthread_once(guard, callback))
abort();
}
#if defined(__APPLE__) && defined(__MACH__)
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
kern_return_t err;
err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
if (err == KERN_SUCCESS)
return 0;
if (err == KERN_INVALID_ARGUMENT)
return -EINVAL;
if (err == KERN_RESOURCE_SHORTAGE)
return -ENOMEM;
abort();
return -EINVAL; /* Satisfy the compiler. */
}
void uv_sem_destroy(uv_sem_t* sem) {
if (semaphore_destroy(mach_task_self(), *sem))
abort();
}
void uv_sem_post(uv_sem_t* sem) {
if (semaphore_signal(*sem))
abort();
}
void uv_sem_wait(uv_sem_t* sem) {
int r;
do
r = semaphore_wait(*sem);
while (r == KERN_ABORTED);
if (r != KERN_SUCCESS)
abort();
}
int uv_sem_trywait(uv_sem_t* sem) {
mach_timespec_t interval;
kern_return_t err;
interval.tv_sec = 0;
interval.tv_nsec = 0;
err = semaphore_timedwait(*sem, interval);
if (err == KERN_SUCCESS)
return 0;
if (err == KERN_OPERATION_TIMED_OUT)
return -EAGAIN;
abort();
return -EINVAL; /* Satisfy the compiler. */
}
#elif defined(__MVS__)
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
uv_sem_t semid;
struct sembuf buf;
int err;
buf.sem_num = 0;
buf.sem_op = value;
buf.sem_flg = 0;
semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR);
if (semid == -1)
return -errno;
if (-1 == semop(semid, &buf, 1)) {
err = errno;
if (-1 == semctl(*sem, 0, IPC_RMID))
abort();
return -err;
}
*sem = semid;
return 0;
}
void uv_sem_destroy(uv_sem_t* sem) {
if (-1 == semctl(*sem, 0, IPC_RMID))
abort();
}
void uv_sem_post(uv_sem_t* sem) {
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = 0;
if (-1 == semop(*sem, &buf, 1))
abort();
}
void uv_sem_wait(uv_sem_t* sem) {
struct sembuf buf;
int op_status;
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = 0;
do
op_status = semop(*sem, &buf, 1);
while (op_status == -1 && errno == EINTR);
if (op_status)
abort();
}
int uv_sem_trywait(uv_sem_t* sem) {
struct sembuf buf;
int op_status;
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = IPC_NOWAIT;
do
op_status = semop(*sem, &buf, 1);
while (op_status == -1 && errno == EINTR);
if (op_status) {
if (errno == EAGAIN)
return -EAGAIN;
abort();
}
return 0;
}
#else /* !(defined(__APPLE__) && defined(__MACH__)) */
int uv_sem_init(uv_sem_t* sem, unsigned int value) {
if (sem_init(sem, 0, value))
return -errno;
return 0;
}
void uv_sem_destroy(uv_sem_t* sem) {
if (sem_destroy(sem))
abort();
}
void uv_sem_post(uv_sem_t* sem) {
if (sem_post(sem))
abort();
}
void uv_sem_wait(uv_sem_t* sem) {
int r;
do
r = sem_wait(sem);
while (r == -1 && errno == EINTR);
if (r)
abort();
}
int uv_sem_trywait(uv_sem_t* sem) {
int r;
do
r = sem_trywait(sem);
while (r == -1 && errno == EINTR);
if (r) {
if (errno == EAGAIN)
return -EAGAIN;
abort();
}
return 0;
}
#endif /* defined(__APPLE__) && defined(__MACH__) */
#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__)
int uv_cond_init(uv_cond_t* cond) {
return -pthread_cond_init(cond, NULL);
}
#else /* !(defined(__APPLE__) && defined(__MACH__)) */
int uv_cond_init(uv_cond_t* cond) {
pthread_condattr_t attr;
int err;
err = pthread_condattr_init(&attr);
if (err)
return -err;
#if !(defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
if (err)
goto error2;
#endif
err = pthread_cond_init(cond, &attr);
if (err)
goto error2;
err = pthread_condattr_destroy(&attr);
if (err)
goto error;
return 0;
error:
pthread_cond_destroy(cond);
error2:
pthread_condattr_destroy(&attr);
return -err;
}
#endif /* defined(__APPLE__) && defined(__MACH__) */
void uv_cond_destroy(uv_cond_t* cond) {
#if defined(__APPLE__) && defined(__MACH__)
/* It has been reported that destroying condition variables that have been
* signalled but not waited on can sometimes result in application crashes.
* See https://codereview.chromium.org/1323293005.
*/
pthread_mutex_t mutex;
struct timespec ts;
int err;
if (pthread_mutex_init(&mutex, NULL))
abort();
if (pthread_mutex_lock(&mutex))
abort();
ts.tv_sec = 0;
ts.tv_nsec = 1;
err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);
if (err != 0 && err != ETIMEDOUT)
abort();
if (pthread_mutex_unlock(&mutex))
abort();
if (pthread_mutex_destroy(&mutex))
abort();
#endif /* defined(__APPLE__) && defined(__MACH__) */
if (pthread_cond_destroy(cond))
abort();
}
void uv_cond_signal(uv_cond_t* cond) {
if (pthread_cond_signal(cond))
abort();
}
void uv_cond_broadcast(uv_cond_t* cond) {
if (pthread_cond_broadcast(cond))
abort();
}
void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
if (pthread_cond_wait(cond, mutex))
abort();
}
int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
int r;
struct timespec ts;
#if defined(__APPLE__) && defined(__MACH__)
ts.tv_sec = timeout / NANOSEC;
ts.tv_nsec = timeout % NANOSEC;
r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
#else
timeout += uv__hrtime(UV_CLOCK_PRECISE);
ts.tv_sec = timeout / NANOSEC;
ts.tv_nsec = timeout % NANOSEC;
#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
/*
* The bionic pthread implementation doesn't support CLOCK_MONOTONIC,
* but has this alternative function instead.
*/
r = pthread_cond_timedwait_monotonic_np(cond, mutex, &ts);
#else
r = pthread_cond_timedwait(cond, mutex, &ts);
#endif /* __ANDROID__ */
#endif
if (r == 0)
return 0;
if (r == ETIMEDOUT)
return -ETIMEDOUT;
abort();
return -EINVAL; /* Satisfy the compiler. */
}
int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
return -pthread_barrier_init(barrier, NULL, count);
}
void uv_barrier_destroy(uv_barrier_t* barrier) {
if (pthread_barrier_destroy(barrier))
abort();
}
int uv_barrier_wait(uv_barrier_t* barrier) {
int r = pthread_barrier_wait(barrier);
if (r && r != PTHREAD_BARRIER_SERIAL_THREAD)
abort();
return r == PTHREAD_BARRIER_SERIAL_THREAD;
}
int uv_key_create(uv_key_t* key) {
return -pthread_key_create(key, NULL);
}
void uv_key_delete(uv_key_t* key) {
if (pthread_key_delete(*key))
abort();
}
void* uv_key_get(uv_key_t* key) {
return pthread_getspecific(*key);
}
void uv_key_set(uv_key_t* key, void* value) {
if (pthread_setspecific(*key, value))
abort();
}
@@ -0,0 +1,172 @@
/* 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 "internal.h"
#include "heap-inl.h"
#include <assert.h>
#include <limits.h>
static int timer_less_than(const struct heap_node* ha,
const struct heap_node* hb) {
const uv_timer_t* a;
const uv_timer_t* b;
a = container_of(ha, uv_timer_t, heap_node);
b = container_of(hb, uv_timer_t, heap_node);
if (a->timeout < b->timeout)
return 1;
if (b->timeout < a->timeout)
return 0;
/* Compare start_id when both have the same timeout. start_id is
* allocated with loop->timer_counter in uv_timer_start().
*/
if (a->start_id < b->start_id)
return 1;
if (b->start_id < a->start_id)
return 0;
return 0;
}
int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
handle->timer_cb = NULL;
handle->repeat = 0;
return 0;
}
int uv_timer_start(uv_timer_t* handle,
uv_timer_cb cb,
uint64_t timeout,
uint64_t repeat) {
uint64_t clamped_timeout;
if (cb == NULL)
return -EINVAL;
if (uv__is_active(handle))
uv_timer_stop(handle);
clamped_timeout = handle->loop->time + timeout;
if (clamped_timeout < timeout)
clamped_timeout = (uint64_t) -1;
handle->timer_cb = cb;
handle->timeout = clamped_timeout;
handle->repeat = repeat;
/* start_id is the second index to be compared in uv__timer_cmp() */
handle->start_id = handle->loop->timer_counter++;
heap_insert((struct heap*) &handle->loop->timer_heap,
(struct heap_node*) &handle->heap_node,
timer_less_than);
uv__handle_start(handle);
return 0;
}
int uv_timer_stop(uv_timer_t* handle) {
if (!uv__is_active(handle))
return 0;
heap_remove((struct heap*) &handle->loop->timer_heap,
(struct heap_node*) &handle->heap_node,
timer_less_than);
uv__handle_stop(handle);
return 0;
}
int uv_timer_again(uv_timer_t* handle) {
if (handle->timer_cb == NULL)
return -EINVAL;
if (handle->repeat) {
uv_timer_stop(handle);
uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
}
return 0;
}
void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) {
handle->repeat = repeat;
}
uint64_t uv_timer_get_repeat(const uv_timer_t* handle) {
return handle->repeat;
}
int uv__next_timeout(const uv_loop_t* loop) {
const struct heap_node* heap_node;
const uv_timer_t* handle;
uint64_t diff;
heap_node = heap_min((const struct heap*) &loop->timer_heap);
if (heap_node == NULL)
return -1; /* block indefinitely */
handle = container_of(heap_node, uv_timer_t, heap_node);
if (handle->timeout <= loop->time)
return 0;
diff = handle->timeout - loop->time;
if (diff > INT_MAX)
diff = INT_MAX;
return diff;
}
void uv__run_timers(uv_loop_t* loop) {
struct heap_node* heap_node;
uv_timer_t* handle;
for (;;) {
heap_node = heap_min((struct heap*) &loop->timer_heap);
if (heap_node == NULL)
break;
handle = container_of(heap_node, uv_timer_t, heap_node);
if (handle->timeout > loop->time)
break;
uv_timer_stop(handle);
uv_timer_again(handle);
handle->timer_cb(handle);
}
}
void uv__timer_close(uv_timer_t* handle) {
uv_timer_stop(handle);
}
+336
View File
@@ -0,0 +1,336 @@
/* 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 "internal.h"
#include "spinlock.h"
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#if defined(__MVS__) && !defined(IMAXBEL)
#define IMAXBEL 0
#endif
static int orig_termios_fd = -1;
static struct termios orig_termios;
static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
static int uv__tty_is_slave(const int fd) {
int result;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
int dummy;
result = ioctl(fd, TIOCGPTN, &dummy) != 0;
#elif defined(__APPLE__)
char dummy[256];
result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
#else
/* Fallback to ptsname
*/
result = ptsname(fd) == NULL;
#endif
return result;
}
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) {
uv_handle_type type;
int flags;
int newfd;
int r;
int saved_flags;
char path[256];
/* File descriptors that refer to files cannot be monitored with epoll.
* That restriction also applies to character devices like /dev/random
* (but obviously not /dev/tty.)
*/
type = uv_guess_handle(fd);
if (type == UV_FILE || type == UV_UNKNOWN_HANDLE)
return -EINVAL;
flags = 0;
newfd = -1;
/* Reopen the file descriptor when it refers to a tty. This lets us put the
* tty in non-blocking mode without affecting other processes that share it
* with us.
*
* Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also
* affects fd 1 of `cat` because both file descriptors refer to the same
* struct file in the kernel. When we reopen our fd 0, it points to a
* different struct file, hence changing its properties doesn't affect
* other processes.
*/
if (type == UV_TTY) {
/* Reopening a pty in master mode won't work either because the reopened
* pty will be in slave mode (*BSD) or reopening will allocate a new
* master/slave pair (Linux). Therefore check if the fd points to a
* slave device.
*/
if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
r = uv__open_cloexec(path, O_RDWR);
else
r = -1;
if (r < 0) {
/* fallback to using blocking writes */
if (!readable)
flags |= UV_STREAM_BLOCKING;
goto skip;
}
newfd = r;
r = uv__dup2_cloexec(newfd, fd);
if (r < 0 && r != -EINVAL) {
/* EINVAL means newfd == fd which could conceivably happen if another
* thread called close(fd) between our calls to isatty() and open().
* That's a rather unlikely event but let's handle it anyway.
*/
uv__close(newfd);
return r;
}
fd = newfd;
}
#if defined(__APPLE__)
/* Save the fd flags in case we need to restore them due to an error. */
do
saved_flags = fcntl(fd, F_GETFL);
while (saved_flags == -1 && errno == EINTR);
if (saved_flags == -1) {
if (newfd != -1)
uv__close(newfd);
return -errno;
}
#endif
/* Pacify the compiler. */
(void) &saved_flags;
skip:
uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
/* If anything fails beyond this point we need to remove the handle from
* the handle queue, since it was added by uv__handle_init in uv_stream_init.
*/
if (!(flags & UV_STREAM_BLOCKING))
uv__nonblock(fd, 1);
#if defined(__APPLE__)
r = uv__stream_try_select((uv_stream_t*) tty, &fd);
if (r) {
int rc = r;
if (newfd != -1)
uv__close(newfd);
QUEUE_REMOVE(&tty->handle_queue);
do
r = fcntl(fd, F_SETFL, saved_flags);
while (r == -1 && errno == EINTR);
return rc;
}
#endif
if (readable)
flags |= UV_STREAM_READABLE;
else
flags |= UV_STREAM_WRITABLE;
uv__stream_open((uv_stream_t*) tty, fd, flags);
tty->mode = UV_TTY_MODE_NORMAL;
return 0;
}
static void uv__tty_make_raw(struct termios* tio) {
assert(tio != NULL);
#if defined __sun || defined __MVS__
/*
* This implementation of cfmakeraw for Solaris and derivatives is taken from
* http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
*/
tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR |
IGNCR | ICRNL | IXON);
tio->c_oflag &= ~OPOST;
tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tio->c_cflag &= ~(CSIZE | PARENB);
tio->c_cflag |= CS8;
#else
cfmakeraw(tio);
#endif /* #ifdef __sun */
}
int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
struct termios tmp;
int fd;
if (tty->mode == (int) mode)
return 0;
fd = uv__stream_fd(tty);
if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
if (tcgetattr(fd, &tty->orig_termios))
return -errno;
/* This is used for uv_tty_reset_mode() */
uv_spinlock_lock(&termios_spinlock);
if (orig_termios_fd == -1) {
orig_termios = tty->orig_termios;
orig_termios_fd = fd;
}
uv_spinlock_unlock(&termios_spinlock);
}
tmp = tty->orig_termios;
switch (mode) {
case UV_TTY_MODE_NORMAL:
break;
case UV_TTY_MODE_RAW:
tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
tmp.c_oflag |= (ONLCR);
tmp.c_cflag |= (CS8);
tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
tmp.c_cc[VMIN] = 1;
tmp.c_cc[VTIME] = 0;
break;
case UV_TTY_MODE_IO:
uv__tty_make_raw(&tmp);
break;
}
/* Apply changes after draining */
if (tcsetattr(fd, TCSADRAIN, &tmp))
return -errno;
tty->mode = mode;
return 0;
}
int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
struct winsize ws;
int err;
do
err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws);
while (err == -1 && errno == EINTR);
if (err == -1)
return -errno;
*width = ws.ws_col;
*height = ws.ws_row;
return 0;
}
uv_handle_type uv_guess_handle(uv_file file) {
struct sockaddr sa;
struct stat s;
socklen_t len;
int type;
if (file < 0)
return UV_UNKNOWN_HANDLE;
if (isatty(file))
return UV_TTY;
if (fstat(file, &s))
return UV_UNKNOWN_HANDLE;
if (S_ISREG(s.st_mode))
return UV_FILE;
if (S_ISCHR(s.st_mode))
return UV_FILE; /* XXX UV_NAMED_PIPE? */
if (S_ISFIFO(s.st_mode))
return UV_NAMED_PIPE;
if (!S_ISSOCK(s.st_mode))
return UV_UNKNOWN_HANDLE;
len = sizeof(type);
if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
return UV_UNKNOWN_HANDLE;
len = sizeof(sa);
if (getsockname(file, &sa, &len))
return UV_UNKNOWN_HANDLE;
if (type == SOCK_DGRAM)
if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
return UV_UDP;
if (type == SOCK_STREAM) {
#if defined(_AIX) || defined(__DragonFly__)
/* on AIX/DragonFly the getsockname call returns an empty sa structure
* for sockets of type AF_UNIX. For all other types it will
* return a properly filled in structure.
*/
if (len == 0)
return UV_NAMED_PIPE;
#endif /* defined(_AIX) || defined(__DragonFly__) */
if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
return UV_TCP;
if (sa.sa_family == AF_UNIX)
return UV_NAMED_PIPE;
}
return UV_UNKNOWN_HANDLE;
}
/* This function is async signal-safe, meaning that it's safe to call from
* inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s
* critical section when the signal was raised.
*/
int uv_tty_reset_mode(void) {
int saved_errno;
int err;
saved_errno = errno;
if (!uv_spinlock_trylock(&termios_spinlock))
return -EBUSY; /* In uv_tty_set_mode(). */
err = 0;
if (orig_termios_fd != -1)
if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios))
err = -errno;
uv_spinlock_unlock(&termios_spinlock);
errno = saved_errno;
return err;
}
+900
View File
@@ -0,0 +1,900 @@
/* 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 "internal.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#if defined(__MVS__)
#include <xti.h>
#endif
#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP)
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
#if defined(IPV6_LEAVE_GROUP) && !defined(IPV6_DROP_MEMBERSHIP)
# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
static void uv__udp_run_completed(uv_udp_t* handle);
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_recvmsg(uv_udp_t* handle);
static void uv__udp_sendmsg(uv_udp_t* handle);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
int domain,
unsigned int flags);
void uv__udp_close(uv_udp_t* handle) {
uv__io_close(handle->loop, &handle->io_watcher);
uv__handle_stop(handle);
if (handle->io_watcher.fd != -1) {
uv__close(handle->io_watcher.fd);
handle->io_watcher.fd = -1;
}
}
void uv__udp_finish_close(uv_udp_t* handle) {
uv_udp_send_t* req;
QUEUE* q;
assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
assert(handle->io_watcher.fd == -1);
while (!QUEUE_EMPTY(&handle->write_queue)) {
q = QUEUE_HEAD(&handle->write_queue);
QUEUE_REMOVE(q);
req = QUEUE_DATA(q, uv_udp_send_t, queue);
req->status = -ECANCELED;
QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
}
uv__udp_run_completed(handle);
assert(handle->send_queue_size == 0);
assert(handle->send_queue_count == 0);
/* Now tear down the handle. */
handle->recv_cb = NULL;
handle->alloc_cb = NULL;
/* but _do not_ touch close_cb */
}
static void uv__udp_run_completed(uv_udp_t* handle) {
uv_udp_send_t* req;
QUEUE* q;
assert(!(handle->flags & UV_UDP_PROCESSING));
handle->flags |= UV_UDP_PROCESSING;
while (!QUEUE_EMPTY(&handle->write_completed_queue)) {
q = QUEUE_HEAD(&handle->write_completed_queue);
QUEUE_REMOVE(q);
req = QUEUE_DATA(q, uv_udp_send_t, queue);
uv__req_unregister(handle->loop, req);
handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
handle->send_queue_count--;
if (req->bufs != req->bufsml)
uv__free(req->bufs);
req->bufs = NULL;
if (req->send_cb == NULL)
continue;
/* req->status >= 0 == bytes written
* req->status < 0 == errno
*/
if (req->status >= 0)
req->send_cb(req, 0);
else
req->send_cb(req, req->status);
}
if (QUEUE_EMPTY(&handle->write_queue)) {
/* Pending queue and completion queue empty, stop watcher. */
uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT);
if (!uv__io_active(&handle->io_watcher, POLLIN))
uv__handle_stop(handle);
}
handle->flags &= ~UV_UDP_PROCESSING;
}
static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
uv_udp_t* handle;
handle = container_of(w, uv_udp_t, io_watcher);
assert(handle->type == UV_UDP);
if (revents & POLLIN)
uv__udp_recvmsg(handle);
if (revents & POLLOUT) {
uv__udp_sendmsg(handle);
uv__udp_run_completed(handle);
}
}
static void uv__udp_recvmsg(uv_udp_t* handle) {
struct sockaddr_storage peer;
struct msghdr h;
ssize_t nread;
uv_buf_t buf;
int flags;
int count;
assert(handle->recv_cb != NULL);
assert(handle->alloc_cb != NULL);
/* Prevent loop starvation when the data comes in as fast as (or faster than)
* we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
*/
count = 32;
memset(&h, 0, sizeof(h));
h.msg_name = &peer;
do {
buf = uv_buf_init(NULL, 0);
handle->alloc_cb((uv_handle_t*) handle, 64 * 1024, &buf);
if (buf.base == NULL || buf.len == 0) {
handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
return;
}
assert(buf.base != NULL);
h.msg_namelen = sizeof(peer);
h.msg_iov = (void*) &buf;
h.msg_iovlen = 1;
do {
nread = recvmsg(handle->io_watcher.fd, &h, 0);
}
while (nread == -1 && errno == EINTR);
if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
handle->recv_cb(handle, 0, &buf, NULL, 0);
else
handle->recv_cb(handle, -errno, &buf, NULL, 0);
}
else {
const struct sockaddr *addr;
if (h.msg_namelen == 0)
addr = NULL;
else
addr = (const struct sockaddr*) &peer;
flags = 0;
if (h.msg_flags & MSG_TRUNC)
flags |= UV_UDP_PARTIAL;
handle->recv_cb(handle, nread, &buf, addr, flags);
}
}
/* recv_cb callback may decide to pause or close the handle */
while (nread != -1
&& count-- > 0
&& handle->io_watcher.fd != -1
&& handle->recv_cb != NULL);
}
static void uv__udp_sendmsg(uv_udp_t* handle) {
uv_udp_send_t* req;
QUEUE* q;
struct msghdr h;
ssize_t size;
while (!QUEUE_EMPTY(&handle->write_queue)) {
q = QUEUE_HEAD(&handle->write_queue);
assert(q != NULL);
req = QUEUE_DATA(q, uv_udp_send_t, queue);
assert(req != NULL);
memset(&h, 0, sizeof h);
h.msg_name = &req->addr;
h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
h.msg_iov = (struct iovec*) req->bufs;
h.msg_iovlen = req->nbufs;
do {
size = sendmsg(handle->io_watcher.fd, &h, 0);
} while (size == -1 && errno == EINTR);
if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
break;
req->status = (size == -1 ? -errno : size);
/* Sending a datagram is an atomic operation: either all data
* is written or nothing is (and EMSGSIZE is raised). That is
* why we don't handle partial writes. Just pop the request
* off the write queue and onto the completed queue, done.
*/
QUEUE_REMOVE(&req->queue);
QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
uv__io_feed(handle->loop, &handle->io_watcher);
}
}
/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
* refinements for programs that use multicast.
*
* Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
* are different from the BSDs: it _shares_ the port rather than steal it
* from the current listener. While useful, it's not something we can emulate
* on other platforms so we don't enable it.
*/
static int uv__set_reuse(int fd) {
int yes;
#if defined(SO_REUSEPORT) && !defined(__linux__)
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
return -errno;
#else
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
return -errno;
#endif
return 0;
}
int uv__udp_bind(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen,
unsigned int flags) {
int err;
int yes;
int fd;
/* Check for bad flags. */
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR))
return -EINVAL;
/* Cannot set IPv6-only mode on non-IPv6 socket. */
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6)
return -EINVAL;
fd = handle->io_watcher.fd;
if (fd == -1) {
err = uv__socket(addr->sa_family, SOCK_DGRAM, 0);
if (err < 0)
return err;
fd = err;
handle->io_watcher.fd = fd;
}
if (flags & UV_UDP_REUSEADDR) {
err = uv__set_reuse(fd);
if (err)
return err;
}
if (flags & UV_UDP_IPV6ONLY) {
#ifdef IPV6_V6ONLY
yes = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) {
err = -errno;
return err;
}
#else
err = -ENOTSUP;
return err;
#endif
}
if (bind(fd, addr, addrlen)) {
err = -errno;
if (errno == EAFNOSUPPORT)
/* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
* socket created with AF_INET to an AF_INET6 address or vice versa. */
err = -EINVAL;
return err;
}
if (addr->sa_family == AF_INET6)
handle->flags |= UV_HANDLE_IPV6;
handle->flags |= UV_HANDLE_BOUND;
return 0;
}
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
int domain,
unsigned int flags) {
union {
struct sockaddr_in6 in6;
struct sockaddr_in in;
struct sockaddr addr;
} taddr;
socklen_t addrlen;
if (handle->io_watcher.fd != -1)
return 0;
switch (domain) {
case AF_INET:
{
struct sockaddr_in* addr = &taddr.in;
memset(addr, 0, sizeof *addr);
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
addrlen = sizeof *addr;
break;
}
case AF_INET6:
{
struct sockaddr_in6* addr = &taddr.in6;
memset(addr, 0, sizeof *addr);
addr->sin6_family = AF_INET6;
addr->sin6_addr = in6addr_any;
addrlen = sizeof *addr;
break;
}
default:
assert(0 && "unsupported address family");
abort();
}
return uv__udp_bind(handle, &taddr.addr, addrlen, flags);
}
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,
unsigned int addrlen,
uv_udp_send_cb send_cb) {
int err;
int empty_queue;
assert(nbufs > 0);
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
/* It's legal for send_queue_count > 0 even when the write_queue is empty;
* it means there are error-state requests in the write_completed_queue that
* will touch up send_queue_size/count later.
*/
empty_queue = (handle->send_queue_count == 0);
uv__req_init(handle->loop, req, UV_UDP_SEND);
assert(addrlen <= sizeof(req->addr));
memcpy(&req->addr, addr, addrlen);
req->send_cb = send_cb;
req->handle = handle;
req->nbufs = nbufs;
req->bufs = req->bufsml;
if (nbufs > ARRAY_SIZE(req->bufsml))
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
if (req->bufs == NULL) {
uv__req_unregister(handle->loop, req);
return -ENOMEM;
}
memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs);
handle->send_queue_count++;
QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue);
uv__handle_start(handle);
if (empty_queue && !(handle->flags & UV_UDP_PROCESSING)) {
uv__udp_sendmsg(handle);
/* `uv__udp_sendmsg` may not be able to do non-blocking write straight
* away. In such cases the `io_watcher` has to be queued for asynchronous
* write.
*/
if (!QUEUE_EMPTY(&handle->write_queue))
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
} else {
uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
}
return 0;
}
int uv__udp_try_send(uv_udp_t* handle,
const uv_buf_t bufs[],
unsigned int nbufs,
const struct sockaddr* addr,
unsigned int addrlen) {
int err;
struct msghdr h;
ssize_t size;
assert(nbufs > 0);
/* already sending a message */
if (handle->send_queue_count != 0)
return -EAGAIN;
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
memset(&h, 0, sizeof h);
h.msg_name = (struct sockaddr*) addr;
h.msg_namelen = addrlen;
h.msg_iov = (struct iovec*) bufs;
h.msg_iovlen = nbufs;
do {
size = sendmsg(handle->io_watcher.fd, &h, 0);
} while (size == -1 && errno == EINTR);
if (size == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return -EAGAIN;
else
return -errno;
}
return size;
}
static int uv__udp_set_membership4(uv_udp_t* handle,
const struct sockaddr_in* multicast_addr,
const char* interface_addr,
uv_membership membership) {
struct ip_mreq mreq;
int optname;
int err;
memset(&mreq, 0, sizeof mreq);
if (interface_addr) {
err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
if (err)
return err;
} else {
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
}
mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
switch (membership) {
case UV_JOIN_GROUP:
optname = IP_ADD_MEMBERSHIP;
break;
case UV_LEAVE_GROUP:
optname = IP_DROP_MEMBERSHIP;
break;
default:
return -EINVAL;
}
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
optname,
&mreq,
sizeof(mreq))) {
#if defined(__MVS__)
if (errno == ENXIO)
return -ENODEV;
#endif
return -errno;
}
return 0;
}
static int uv__udp_set_membership6(uv_udp_t* handle,
const struct sockaddr_in6* multicast_addr,
const char* interface_addr,
uv_membership membership) {
int optname;
struct ipv6_mreq mreq;
struct sockaddr_in6 addr6;
memset(&mreq, 0, sizeof mreq);
if (interface_addr) {
if (uv_ip6_addr(interface_addr, 0, &addr6))
return -EINVAL;
mreq.ipv6mr_interface = addr6.sin6_scope_id;
} else {
mreq.ipv6mr_interface = 0;
}
mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr;
switch (membership) {
case UV_JOIN_GROUP:
optname = IPV6_ADD_MEMBERSHIP;
break;
case UV_LEAVE_GROUP:
optname = IPV6_DROP_MEMBERSHIP;
break;
default:
return -EINVAL;
}
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IPV6,
optname,
&mreq,
sizeof(mreq))) {
#if defined(__MVS__)
if (errno == ENXIO)
return -ENODEV;
#endif
return -errno;
}
return 0;
}
int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
int domain;
int err;
int fd;
/* Use the lower 8 bits for the domain */
domain = flags & 0xFF;
if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
return -EINVAL;
if (flags & ~0xFF)
return -EINVAL;
if (domain != AF_UNSPEC) {
err = uv__socket(domain, SOCK_DGRAM, 0);
if (err < 0)
return err;
fd = err;
} else {
fd = -1;
}
uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP);
handle->alloc_cb = NULL;
handle->recv_cb = NULL;
handle->send_queue_size = 0;
handle->send_queue_count = 0;
uv__io_init(&handle->io_watcher, uv__udp_io, fd);
QUEUE_INIT(&handle->write_queue);
QUEUE_INIT(&handle->write_completed_queue);
return 0;
}
int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
return uv_udp_init_ex(loop, handle, AF_UNSPEC);
}
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
int err;
/* Check for already active socket. */
if (handle->io_watcher.fd != -1)
return -EBUSY;
err = uv__nonblock(sock, 1);
if (err)
return err;
err = uv__set_reuse(sock);
if (err)
return err;
handle->io_watcher.fd = sock;
return 0;
}
int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
uv_membership membership) {
int err;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) {
err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
if (err)
return err;
return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
} else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) {
err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
if (err)
return err;
return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
} else {
return -EINVAL;
}
}
static int uv__setsockopt(uv_udp_t* handle,
int option4,
int option6,
const void* val,
size_t size) {
int r;
if (handle->flags & UV_HANDLE_IPV6)
r = setsockopt(handle->io_watcher.fd,
IPPROTO_IPV6,
option6,
val,
size);
else
r = setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
option4,
val,
size);
if (r)
return -errno;
return 0;
}
static int uv__setsockopt_maybe_char(uv_udp_t* handle,
int option4,
int option6,
int val) {
#if defined(__sun) || defined(_AIX) || defined(__MVS__)
char arg = val;
#elif defined(__OpenBSD__)
unsigned char arg = val;
#else
int arg = val;
#endif
if (val < 0 || val > 255)
return -EINVAL;
return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg));
}
int uv_udp_set_broadcast(uv_udp_t* handle, int on) {
if (setsockopt(handle->io_watcher.fd,
SOL_SOCKET,
SO_BROADCAST,
&on,
sizeof(on))) {
return -errno;
}
return 0;
}
int uv_udp_set_ttl(uv_udp_t* handle, int ttl) {
if (ttl < 1 || ttl > 255)
return -EINVAL;
#if defined(__MVS__)
if (!(handle->flags & UV_HANDLE_IPV6))
return -ENOTSUP; /* zOS does not support setting ttl for IPv4 */
#endif
/*
* On Solaris and derivatives such as SmartOS, the length of socket options
* is sizeof(int) for IP_TTL and IPV6_UNICAST_HOPS,
* so hardcode the size of these options on this platform,
* and use the general uv__setsockopt_maybe_char call on other platforms.
*/
#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
defined(__MVS__)
return uv__setsockopt(handle,
IP_TTL,
IPV6_UNICAST_HOPS,
&ttl,
sizeof(ttl));
#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
defined(__MVS__) */
return uv__setsockopt_maybe_char(handle,
IP_TTL,
IPV6_UNICAST_HOPS,
ttl);
}
int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) {
/*
* On Solaris and derivatives such as SmartOS, the length of socket options
* is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for
* IP_MULTICAST_TTL, so hardcode the size of the option in the IPv6 case,
* and use the general uv__setsockopt_maybe_char call otherwise.
*/
#if defined(__sun) || defined(_AIX) || defined(__MVS__)
if (handle->flags & UV_HANDLE_IPV6)
return uv__setsockopt(handle,
IP_MULTICAST_TTL,
IPV6_MULTICAST_HOPS,
&ttl,
sizeof(ttl));
#endif /* defined(__sun) || defined(_AIX) || defined(__MVS__) */
return uv__setsockopt_maybe_char(handle,
IP_MULTICAST_TTL,
IPV6_MULTICAST_HOPS,
ttl);
}
int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) {
/*
* On Solaris and derivatives such as SmartOS, the length of socket options
* is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for
* IP_MULTICAST_LOOP, so hardcode the size of the option in the IPv6 case,
* and use the general uv__setsockopt_maybe_char call otherwise.
*/
#if defined(__sun) || defined(_AIX) || defined(__MVS__)
if (handle->flags & UV_HANDLE_IPV6)
return uv__setsockopt(handle,
IP_MULTICAST_LOOP,
IPV6_MULTICAST_LOOP,
&on,
sizeof(on));
#endif /* defined(__sun) || defined(_AIX) || defined(__MVS__) */
return uv__setsockopt_maybe_char(handle,
IP_MULTICAST_LOOP,
IPV6_MULTICAST_LOOP,
on);
}
int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
struct sockaddr_storage addr_st;
struct sockaddr_in* addr4;
struct sockaddr_in6* addr6;
addr4 = (struct sockaddr_in*) &addr_st;
addr6 = (struct sockaddr_in6*) &addr_st;
if (!interface_addr) {
memset(&addr_st, 0, sizeof addr_st);
if (handle->flags & UV_HANDLE_IPV6) {
addr_st.ss_family = AF_INET6;
addr6->sin6_scope_id = 0;
} else {
addr_st.ss_family = AF_INET;
addr4->sin_addr.s_addr = htonl(INADDR_ANY);
}
} else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) {
/* nothing, address was parsed */
} else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) {
/* nothing, address was parsed */
} else {
return -EINVAL;
}
if (addr_st.ss_family == AF_INET) {
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
IP_MULTICAST_IF,
(void*) &addr4->sin_addr,
sizeof(addr4->sin_addr)) == -1) {
return -errno;
}
} else if (addr_st.ss_family == AF_INET6) {
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IPV6,
IPV6_MULTICAST_IF,
&addr6->sin6_scope_id,
sizeof(addr6->sin6_scope_id)) == -1) {
return -errno;
}
} else {
assert(0 && "unexpected address family");
abort();
}
return 0;
}
int uv_udp_getsockname(const uv_udp_t* handle,
struct sockaddr* name,
int* namelen) {
socklen_t socklen;
if (handle->io_watcher.fd == -1)
return -EINVAL; /* FIXME(bnoordhuis) -EBADF */
/* sizeof(socklen_t) != sizeof(int) on some systems. */
socklen = (socklen_t) *namelen;
if (getsockname(handle->io_watcher.fd, name, &socklen))
return -errno;
*namelen = (int) socklen;
return 0;
}
int uv__udp_recv_start(uv_udp_t* handle,
uv_alloc_cb alloc_cb,
uv_udp_recv_cb recv_cb) {
int err;
if (alloc_cb == NULL || recv_cb == NULL)
return -EINVAL;
if (uv__io_active(&handle->io_watcher, POLLIN))
return -EALREADY; /* FIXME(bnoordhuis) Should be -EBUSY. */
err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0);
if (err)
return err;
handle->alloc_cb = alloc_cb;
handle->recv_cb = recv_cb;
uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
uv__handle_start(handle);
return 0;
}
int uv__udp_recv_stop(uv_udp_t* handle) {
uv__io_stop(handle->loop, &handle->io_watcher, POLLIN);
if (!uv__io_active(&handle->io_watcher, POLLOUT))
uv__handle_stop(handle);
handle->alloc_cb = NULL;
handle->recv_cb = NULL;
return 0;
}