1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/lib/lws-plat-unix.c
Andy Green 4b85c1d4ac api rationalization: eliminate all libwebsocket[s]_ prefixes
This nukes all the oldstyle prefixes except in the compatibility code.

struct libwebsockets becomes struct lws too.

The api docs are updated accordingly as are the READMEs that mention
those apis.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 11:08:32 +08:00

475 lines
10 KiB
C

#include "private-libwebsockets.h"
#include <pwd.h>
#include <grp.h>
/*
* included from libwebsockets.c for unix builds
*/
unsigned long long time_in_microseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
}
LWS_VISIBLE int lws_get_random(struct lws_context *context,
void *buf, int len)
{
return read(context->fd_random, (char *)buf, len);
}
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
{
struct lws_pollfd fds;
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi->truncated_send_len)
return 1;
fds.fd = wsi->sock;
fds.events = POLLOUT;
fds.revents = 0;
if (poll(&fds, 1, 0) != 1)
return 1;
if ((fds.revents & POLLOUT) == 0)
return 1;
/* okay to send another packet without blocking */
return 0;
}
LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
{
return poll(fd, 1, 0);
}
/*
* This is just used to interrupt poll waiting
* we don't have to do anything with it.
*/
static void lws_sigusr2(int sig)
{
}
/**
* lws_cancel_service() - Cancel servicing of pending websocket activity
* @context: Websocket context
*
* This function let a call to lws_service() waiting for a timeout
* immediately return.
*/
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
char buf = 0;
if (write(context->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
int syslog_level = LOG_DEBUG;
switch (level) {
case LLL_ERR:
syslog_level = LOG_ERR;
break;
case LLL_WARN:
syslog_level = LOG_WARNING;
break;
case LLL_NOTICE:
syslog_level = LOG_NOTICE;
break;
case LLL_INFO:
syslog_level = LOG_INFO;
break;
}
syslog(syslog_level, "%s", line);
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
int n;
int m;
char buf;
#ifdef LWS_OPENSSL_SUPPORT
struct lws *wsi, *wsi_next;
#endif
/* stay dead once we are dead */
if (!context)
return 1;
lws_libev_run(context);
context->service_tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
#ifdef LWS_OPENSSL_SUPPORT
/* if we know we have non-network pending data, do not wait in poll */
if (lws_ssl_anybody_has_buffered_read(context))
timeout_ms = 0;
#endif
n = poll(context->fds, context->fds_count, timeout_ms);
context->service_tid = 0;
#ifdef LWS_OPENSSL_SUPPORT
if (!lws_ssl_anybody_has_buffered_read(context) && n == 0) {
#else
if (n == 0) /* poll timeout */ {
#endif
lws_service_fd(context, NULL);
return 0;
}
if (n < 0) {
if (LWS_ERRNO != LWS_EINTR)
return -1;
return 0;
}
#ifdef LWS_OPENSSL_SUPPORT
/*
* For all guys with buffered SSL read data already saved up, if they
* are not flowcontrolled, fake their POLLIN status so they'll get
* service to use up the buffered incoming data, even though their
* network socket may have nothing
*/
wsi = context->pending_read_list;
while (wsi) {
wsi_next = wsi->pending_read_list_next;
context->fds[wsi->position_in_fds_table].revents |=
context->fds[wsi->position_in_fds_table].events & POLLIN;
if (context->fds[wsi->position_in_fds_table].revents & POLLIN) {
/*
* he's going to get serviced now, take him off the
* list of guys with buffered SSL. If he still has some
* at the end of the service, he'll get put back on the
* list then.
*/
lws_ssl_remove_wsi_from_buffered_list(context, wsi);
}
wsi = wsi_next;
}
#endif
/* any socket with events to service? */
for (n = 0; n < context->fds_count; n++) {
if (!context->fds[n].revents)
continue;
if (context->fds[n].fd == context->dummy_pipe_fds[0]) {
if (read(context->fds[n].fd, &buf, 1) != 1)
lwsl_err("Cannot read from dummy pipe.");
continue;
}
m = lws_service_fd(context, &context->fds[n]);
if (m < 0)
return -1;
/* if something closed, retry this slot */
if (m)
n--;
}
return 0;
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_context *context, int fd)
{
int optval = 1;
socklen_t optlen = sizeof(optval);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__OpenBSD__)
struct protoent *tcp_proto;
#endif
if (context->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const void *)&optval, optlen) < 0)
return 1;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__CYGWIN__) || defined(__OpenBSD__)
/*
* didn't find a way to set these per-socket, need to
* tune kernel systemwide values
*/
#else
/* set the keepalive conditions we want on it too */
optval = context->ka_time;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
return 1;
optval = context->ka_interval;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
(const void *)&optval, optlen) < 0)
return 1;
optval = context->ka_probes;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
(const void *)&optval, optlen) < 0)
return 1;
#endif
}
/* Disable Nagle */
optval = 1;
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
!defined(__OpenBSD__)
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#else
tcp_proto = getprotobyname("TCP");
if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0)
return 1;
#endif
/* We are nonblocking... */
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
return 1;
return 0;
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
if (info->uid != -1) {
struct passwd *p = getpwuid(info->uid);
if (p) {
initgroups(p->pw_name, info->gid);
if (setuid(info->uid))
lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
else
lwsl_notice(" Set privs to user '%s'\n", p->pw_name);
} else
lwsl_warn("getpwuid: unable to find uid %d", info->uid);
}
if (info->gid != -1)
if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
}
LWS_VISIBLE int
lws_plat_init_lookup(struct lws_context *context)
{
context->lws_lookup = lws_zalloc(sizeof(struct lws *) * context->max_fds);
if (context->lws_lookup == NULL) {
lwsl_err(
"Unable to allocate lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
return 0;
}
LWS_VISIBLE int
lws_plat_init_fd_tables(struct lws_context *context)
{
context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
if (context->fd_random < 0) {
lwsl_err("Unable to open random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, context->fd_random);
return 1;
}
if (lws_libev_init_fd_table(context))
/* libev handled it instead */
return 0;
if (pipe(context->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
context->fds[0].fd = context->dummy_pipe_fds[0];
context->fds[0].events = LWS_POLLIN;
context->fds[0].revents = 0;
context->fds_count = 1;
return 0;
}
static void sigpipe_handler(int x)
{
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
sigset_t mask;
signal(SIGUSR2, lws_sigusr2);
sigemptyset(&mask);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, NULL);
signal(SIGPIPE, sigpipe_handler);
return 0;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
{
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
{
if (context->lws_lookup)
lws_free(context->lws_lookup);
close(context->dummy_pipe_fds[0]);
close(context->dummy_pipe_fds[1]);
close(context->fd_random);
}
/* cast a struct sockaddr_in6 * into addr for ipv6 */
LWS_VISIBLE int
interface_to_sa(struct lws_context *context,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
int rc = -1;
struct ifaddrs *ifr;
struct ifaddrs *ifc;
#ifdef LWS_USE_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
#endif
getifaddrs(&ifr);
for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) {
if (!ifc->ifa_addr)
continue;
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
if (strcmp(ifc->ifa_name, ifname))
continue;
switch (ifc->ifa_addr->sa_family) {
case AF_INET:
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
/* map IPv4 to IPv6 */
bzero((char *)&addr6->sin6_addr,
sizeof(struct in6_addr));
addr6->sin6_addr.s6_addr[10] = 0xff;
addr6->sin6_addr.s6_addr[11] = 0xff;
memcpy(&addr6->sin6_addr.s6_addr[12],
&((struct sockaddr_in *)ifc->ifa_addr)->sin_addr,
sizeof(struct in_addr));
} else
#endif
memcpy(addr,
(struct sockaddr_in *)ifc->ifa_addr,
sizeof(struct sockaddr_in));
break;
#ifdef LWS_USE_IPV6
case AF_INET6:
memcpy(&addr6->sin6_addr,
&((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
sizeof(struct in6_addr));
break;
#endif
default:
continue;
}
rc = 0;
}
freeifaddrs(ifr);
if (rc == -1) {
/* check if bind to IP adddress */
#ifdef LWS_USE_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
rc = 0;
else
#endif
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
rc = 0;
}
return rc;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context,
struct lws *wsi)
{
lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ);
context->fds[context->fds_count++].revents = 0;
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
{
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
{
/* if our parent went down, don't linger around */
if (context->started_with_parent &&
kill(context->started_with_parent, 0) < 0)
kill(getpid(), SIGTERM);
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
{
return 0;
}
LWS_VISIBLE int
lws_plat_open_file(const char* filename, unsigned long* filelen)
{
struct stat stat_buf;
int ret = open(filename, O_RDONLY);
if (ret < 0)
return LWS_INVALID_FILE;
if (fstat(ret, &stat_buf) < 0) {
close(ret);
return LWS_INVALID_FILE;
}
*filelen = stat_buf.st_size;
return ret;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
return inet_ntop(af, src, dst, cnt);
}