detect service tid once and use wsi with valid context to do it
[platform/upstream/libwebsockets.git] / lib / lws-plat-unix.c
index 28a7ed5..23c11b0 100644 (file)
@@ -1,5 +1,8 @@
 #include "private-libwebsockets.h"
 
+#include <pwd.h>
+#include <grp.h>
+
 /*
  * included from libwebsockets.c for unix builds
  */
@@ -8,21 +11,22 @@ unsigned long long time_in_microseconds(void)
 {
        struct timeval tv;
        gettimeofday(&tv, NULL);
-       return (tv.tv_sec * 1000000) + tv.tv_usec;
+       return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
 }
 
-LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context,
-                                                            void *buf, int len)
+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 libwebsocket *wsi)
+LWS_VISIBLE int
+lws_send_pipe_choked(struct lws *wsi)
 {
-       struct libwebsocket_pollfd fds;
+       struct lws_pollfd fds;
 
        /* treat the fact we got a truncated send pending as if we're choked */
-       if (wsi->truncated_send_len)
+       if (wsi->trunc_len)
                return 1;
 
        fds.fd = wsi->sock;
@@ -41,7 +45,7 @@ LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi)
 }
 
 LWS_VISIBLE int
-lws_poll_listen_fd(struct libwebsocket_pollfd *fd)
+lws_poll_listen_fd(struct lws_pollfd *fd)
 {
        return poll(fd, 1, 0);
 }
@@ -55,14 +59,14 @@ static void lws_sigusr2(int sig)
 }
 
 /**
- * libwebsocket_cancel_service() - Cancel servicing of pending websocket activity
+ * lws_cancel_service() - Cancel servicing of pending websocket activity
  * @context:   Websocket context
  *
- *     This function let a call to libwebsocket_service() waiting for a timeout
+ *     This function let a call to lws_service() waiting for a timeout
  *     immediately return.
  */
 LWS_VISIBLE void
-libwebsocket_cancel_service(struct libwebsocket_context *context)
+lws_cancel_service(struct lws_context *context)
 {
        char buf = 0;
 
@@ -92,11 +96,14 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
 }
 
 LWS_VISIBLE int
-lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
+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 */
 
@@ -105,14 +112,31 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
 
        lws_libev_run(context);
 
-       context->service_tid = context->protocols[0].callback(context, NULL,
-                                    LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
+       if (!context->service_tid_detected) {
+               struct lws _lws;
+
+               memset(&_lws, 0, sizeof(_lws));
+               _lws.context = context;
+
+               context->service_tid_detected = context->protocols[0].callback(
+                       &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
+       }
+       context->service_tid = context->service_tid_detected;
 
+#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 */ {
-               libwebsocket_service_fd(context, NULL);
+#endif
+               lws_service_fd(context, NULL);
                return 0;
        }
 
@@ -122,6 +146,32 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
                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(wsi);
+
+               wsi = wsi_next;
+       }
+#endif
+
        /* any socket with events to service? */
 
        for (n = 0; n < context->fds_count; n++) {
@@ -134,7 +184,7 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
                        continue;
                }
 
-               m = libwebsocket_service_fd(context, &context->fds[n]);
+               m = lws_service_fd(context, &context->fds[n]);
                if (m < 0)
                        return -1;
                /* if something closed, retry this slot */
@@ -146,12 +196,13 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
 }
 
 LWS_VISIBLE int
-lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
+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__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+    defined(__OpenBSD__)
        struct protoent *tcp_proto;
 #endif
 
@@ -159,10 +210,11 @@ lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
                /* enable keepalive on this socket */
                optval = 1;
                if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
-                                            (const void *)&optval, optlen) < 0)
+                              (const void *)&optval, optlen) < 0)
                        return 1;
 
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+        defined(__CYGWIN__) || defined(__OpenBSD__)
 
                /*
                 * didn't find a way to set these per-socket, need to
@@ -171,33 +223,37 @@ lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
 #else
                /* set the keepalive conditions we want on it too */
                optval = context->ka_time;
-               if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
-                                            (const void *)&optval, optlen) < 0)
+               if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
+                              (const void *)&optval, optlen) < 0)
                        return 1;
 
                optval = context->ka_interval;
-               if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
-                                            (const void *)&optval, optlen) < 0)
+               if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
+                              (const void *)&optval, optlen) < 0)
                        return 1;
 
                optval = context->ka_probes;
-               if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
-                                            (const void *)&optval, optlen) < 0)
+               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__)
-       setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
+#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");
-       setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
+       if (setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen) < 0)
+               return 1;
 #endif
 
        /* We are nonblocking... */
-       fcntl(fd, F_SETFL, O_NONBLOCK);
+       if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+               return 1;
 
        return 0;
 }
@@ -205,47 +261,28 @@ lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
 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));
-       if (info->uid != -1)
-               if (setuid(info->uid))
-                       lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO)); 
-}
-
-LWS_VISIBLE int
-lws_plat_init_fd_tables(struct libwebsocket_context *context)
-{
-       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;
-       
-       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;
-       }
 
-       return 0;
 }
 
 static void sigpipe_handler(int x)
 {
 }
 
-
 LWS_VISIBLE int
 lws_plat_context_early_init(void)
 {
@@ -256,20 +293,23 @@ lws_plat_context_early_init(void)
        sigaddset(&mask, SIGUSR2);
 
        sigprocmask(SIG_BLOCK, &mask, NULL);
-       
+
        signal(SIGPIPE, sigpipe_handler);
 
        return 0;
 }
 
 LWS_VISIBLE void
-lws_plat_context_early_destroy(struct libwebsocket_context *context)
+lws_plat_context_early_destroy(struct lws_context *context)
 {
 }
 
 LWS_VISIBLE void
-lws_plat_context_late_destroy(struct libwebsocket_context *context)
+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);
@@ -278,7 +318,7 @@ lws_plat_context_late_destroy(struct libwebsocket_context *context)
 /* cast a struct sockaddr_in6 * into addr for ipv6 */
 
 LWS_VISIBLE int
-interface_to_sa(struct libwebsocket_context *context,
+interface_to_sa(struct lws_context *context,
                const char *ifname, struct sockaddr_in *addr, size_t addrlen)
 {
        int rc = -1;
@@ -319,8 +359,6 @@ interface_to_sa(struct libwebsocket_context *context,
                        break;
 #ifdef LWS_USE_IPV6
                case AF_INET6:
-                       if (rc >= 0)
-                               break;
                        memcpy(&addr6->sin6_addr,
                          &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
                                                       sizeof(struct in6_addr));
@@ -333,7 +371,7 @@ interface_to_sa(struct libwebsocket_context *context,
        }
 
        freeifaddrs(ifr);
-       
+
        if (rc == -1) {
                /* check if bind to IP adddress */
 #ifdef LWS_USE_IPV6
@@ -349,53 +387,144 @@ interface_to_sa(struct libwebsocket_context *context,
 }
 
 LWS_VISIBLE void
-lws_plat_insert_socket_into_fds(struct libwebsocket_context *context,
-                                                      struct libwebsocket *wsi)
+lws_plat_insert_socket_into_fds(struct lws_context *context,
+                                                      struct lws *wsi)
 {
-       lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ);
+       lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
        context->fds[context->fds_count++].revents = 0;
 }
 
 LWS_VISIBLE void
-lws_plat_delete_socket_from_fds(struct libwebsocket_context *context,
-                                               struct libwebsocket *wsi, int m)
+lws_plat_delete_socket_from_fds(struct lws_context *context,
+                                               struct lws *wsi, int m)
 {
 }
 
 LWS_VISIBLE void
-lws_plat_service_periodic(struct libwebsocket_context *context)
+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(context->started_with_parent, 0) < 0)
                kill(getpid(), SIGTERM);
 }
 
 LWS_VISIBLE int
-lws_plat_change_pollfd(struct libwebsocket_context *context,
-                     struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd)
+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)
+LWS_VISIBLE const char *
+lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
+{
+       return inet_ntop(af, src, dst, cnt);
+}
+
+static lws_filefd_type
+_lws_plat_file_open(struct lws *wsi, const char *filename,
+                   unsigned long *filelen, int flags)
 {
        struct stat stat_buf;
-       int ret = open(filename, O_RDONLY);
+       int ret = open(filename, flags, 0664);
 
        if (ret < 0)
                return LWS_INVALID_FILE;
 
-       fstat(ret, &stat_buf);
+       if (fstat(ret, &stat_buf) < 0) {
+               close(ret);
+               return LWS_INVALID_FILE;
+       }
        *filelen = stat_buf.st_size;
        return ret;
 }
 
-#ifdef LWS_USE_IPV6
-LWS_VISIBLE const char *
-lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
-{ 
-       return inet_ntop(af, src, dst, cnt);
+static int
+_lws_plat_file_close(struct lws *wsi, lws_filefd_type fd)
+{
+       return close(fd);
+}
+
+unsigned long
+_lws_plat_file_seek_cur(struct lws *wsi, lws_filefd_type fd, long offset)
+{
+       return lseek(fd, offset, SEEK_CUR);
+}
+
+static int
+_lws_plat_file_read(struct lws *wsi, lws_filefd_type fd, unsigned long *amount,
+                   unsigned char *buf, unsigned long len)
+{
+       long n;
+
+       n = read((int)fd, buf, len);
+       if (n == -1) {
+               *amount = 0;
+               return -1;
+       }
+
+       *amount = n;
+
+       return 0;
+}
+
+static int
+_lws_plat_file_write(struct lws *wsi, lws_filefd_type fd, unsigned long *amount,
+                    unsigned char *buf, unsigned long len)
+{
+       long n;
+
+       n = write((int)fd, buf, len);
+       if (n == -1) {
+               *amount = 0;
+               return -1;
+       }
+
+       *amount = n;
+
+       return 0;
+}
+
+LWS_VISIBLE int
+lws_plat_init(struct lws_context *context,
+             struct lws_context_creation_info *info)
+{
+       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;
+       }
+
+       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)) {
+               /* otherwise libev handled it instead */
+
+               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;
+
+       context->fops.open      = _lws_plat_file_open;
+       context->fops.close     = _lws_plat_file_close;
+       context->fops.seek_cur  = _lws_plat_file_seek_cur;
+       context->fops.read      = _lws_plat_file_read;
+       context->fops.write     = _lws_plat_file_write;
+
+       return 0;
 }
-#endif