vhost: allow adding vhosts after server init
authorBablooos <beetle@gambler.ru>
Tue, 29 Nov 2016 23:05:13 +0000 (07:05 +0800)
committerAndy Green <andy@warmcat.com>
Fri, 16 Dec 2016 14:08:13 +0000 (22:08 +0800)
This should allow adding vhosts "late", ie, after the server is up and
running with its initial vhost(s).  The necessary housekeeping is folded
into lws_create_vhost() itself so it should be transparent.

Notice though that at the point the server starts to do service after it
starts initially, if it was requested that the UID / GID change, that
is performed at that point and is not reversible.

So vhosts added "late" find themselves running under the unprivileged
UID / GID from the very start, whereas vhosts added "early" initially
run under the UID / GID the process started with.  If protocols the
vhost uses want to, eg, open privileged files at init and then use
them unprivileged, that will fail if the vhost is added late because
the initial privs are already gone.

AG: also deal with lws_protocol_init() on late vhost init (does the
callbacks for per vh protocol creation), add comments

lib/context.c
lib/libuv.c
lib/private-libwebsockets.h
lib/server.c

index 17615fb..88e7fe4 100644 (file)
@@ -122,6 +122,10 @@ lws_vhost_protocol_options(struct lws_vhost *vh, const char *name)
        return NULL;
 }
 
+/*
+ * inform every vhost that hasn't already done it, that
+ * his protocols are initializing
+ */
 int
 lws_protocol_init(struct lws_context *context)
 {
@@ -138,6 +142,10 @@ lws_protocol_init(struct lws_context *context)
        while (vh) {
                wsi.vhost = vh;
 
+               /* only do the protocol init once for a given vhost */
+               if (vh->created_vhost_protocols)
+                       goto next;
+
                /* initialize supported protocols on this vhost */
 
                for (n = 0; n < vh->count_protocols; n++) {
@@ -185,11 +193,15 @@ lws_protocol_init(struct lws_context *context)
                                return 1;
                }
 
+               vh->created_vhost_protocols = 1;
+next:
                vh = vh->vhost_next;
        }
 
+       if (!context->protocol_init_done)
+               lws_finalize_startup(context);
+
        context->protocol_init_done = 1;
-       lws_finalize_startup(context);
 
        return 0;
 }
@@ -520,6 +532,11 @@ lws_create_vhost(struct lws_context *context,
                vh1 = &(*vh1)->vhost_next;
        };
 
+       /* for the case we are adding a vhost much later, after server init */
+
+       if (context->protocol_init_done)
+               lws_protocol_init(context);
+
        return vh;
 
 bail:
index 742ca87..4e80936 100644 (file)
@@ -143,47 +143,91 @@ lws_uv_timeout_cb(uv_timer_t *timer
 
 static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE };
 
+int
+lws_uv_initvhost(struct lws_vhost* vh, struct lws* wsi)
+{
+       struct lws_context_per_thread *pt;
+       int n;
+
+       if (!LWS_LIBUV_ENABLED(vh->context))
+               return 0;
+       if (!wsi)
+               wsi = vh->lserv_wsi;
+       if (!wsi)
+               return 0;
+       if (wsi->w_read.context)
+               return 0;
+
+       pt = &vh->context->pt[(int)wsi->tsi];
+       if (!pt->io_loop_uv)
+               return 0;
+
+       wsi->w_read.context = vh->context;
+       n = uv_poll_init_socket(pt->io_loop_uv,
+                               &wsi->w_read.uv_watcher, wsi->sock);
+       if (n) {
+               lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
+                                n, (void *)(long)wsi->sock);
+
+               return -1;
+       }
+       lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
+
+       return 0;
+}
+
+/*
+ * This needs to be called after vhosts have been defined.
+ *
+ * If later, after server start, another vhost is added, this must be
+ * called again to bind the vhost
+ */
+
 LWS_VISIBLE int
 lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
 {
        struct lws_context_per_thread *pt = &context->pt[tsi];
        struct lws_vhost *vh = context->vhost_list;
-       int status = 0, n, ns;
+       int status = 0, n, ns, first = 1;
 
-       if (!loop) {
-               loop = lws_malloc(sizeof(*loop));
+       if (!pt->io_loop_uv) {
                if (!loop) {
-                       lwsl_err("OOM\n");
-                       return -1;
+                       loop = lws_malloc(sizeof(*loop));
+                       if (!loop) {
+                               lwsl_err("OOM\n");
+                               return -1;
+                       }
+       #if UV_VERSION_MAJOR > 0
+                       uv_loop_init(loop);
+       #else
+                       lwsl_err("This libuv is too old to work...\n");
+                       return 1;
+       #endif
+                       pt->ev_loop_foreign = 0;
+               } else {
+                       lwsl_notice(" Using foreign event loop...\n");
+                       pt->ev_loop_foreign = 1;
                }
-#if UV_VERSION_MAJOR > 0
-               uv_loop_init(loop);
-#else
-               lwsl_err("This libuv is too old to work...\n");
-               return 1;
-#endif
-               pt->ev_loop_foreign = 0;
-       } else {
-               lwsl_notice(" Using foreign event loop...\n");
-               pt->ev_loop_foreign = 1;
-       }
 
-       pt->io_loop_uv = loop;
-       uv_idle_init(loop, &pt->uv_idle);
+               pt->io_loop_uv = loop;
+               uv_idle_init(loop, &pt->uv_idle);
 
-       ns = ARRAY_SIZE(sigs);
-       if (lws_check_opt(context->options, LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
-               ns = 2;
+               ns = ARRAY_SIZE(sigs);
+               if (lws_check_opt(context->options,
+                                 LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
+                       ns = 2;
 
-       if (pt->context->use_ev_sigint) {
-               assert(ns <= ARRAY_SIZE(pt->signals));
-               for (n = 0; n < ns; n++) {
-                       uv_signal_init(loop, &pt->signals[n]);
-                       pt->signals[n].data = pt->context;
-                       uv_signal_start(&pt->signals[n],
-                                       context->lws_uv_sigint_cb, sigs[n]);
+               if (pt->context->use_ev_sigint) {
+                       assert(ns <= ARRAY_SIZE(pt->signals));
+                       for (n = 0; n < ns; n++) {
+                               uv_signal_init(loop, &pt->signals[n]);
+                               pt->signals[n].data = pt->context;
+                               uv_signal_start(&pt->signals[n],
+                                               context->lws_uv_sigint_cb, sigs[n]);
+                       }
                }
-       }
+       } else
+               first = 0;
 
        /*
         * Initialize the accept wsi read watcher with all the listening sockets
@@ -193,24 +237,16 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
         * initialized until after context creation.
         */
        while (vh) {
-               if (vh->lserv_wsi) {
-                       vh->lserv_wsi->w_read.context = context;
-                       n = uv_poll_init_socket(pt->io_loop_uv,
-                                               &vh->lserv_wsi->w_read.uv_watcher,
-                                               vh->lserv_wsi->sock);
-                       if (n) {
-                               lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
-                                       n, (void *)(long)vh->lserv_wsi->sock);
-
-                               return -1;
-                       }
-                       lws_libuv_io(vh->lserv_wsi, LWS_EV_START | LWS_EV_READ);
-               }
+               if (lws_uv_initvhost(vh, vh->lserv_wsi) == -1)
+                       return -1;
                vh = vh->vhost_next;
        }
 
-       uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
-       uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb, 10, 1000);
+       if (first) {
+               uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
+               uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb,
+                              10, 1000);
+       }
 
        return status;
 }
index 6162d55..6a3ad8c 100644 (file)
@@ -800,6 +800,8 @@ struct lws_vhost {
        unsigned int user_supplied_ssl_ctx:1;
 #endif
 
+       unsigned int created_vhost_protocols:1;
+
        unsigned char default_protocol_index;
 };
 
@@ -965,6 +967,8 @@ LWS_EXTERN void
 lws_libuv_run(const struct lws_context *context, int tsi);
 LWS_EXTERN void
 lws_libuv_destroyloop(struct lws_context *context, int tsi);
+LWS_EXTERN int
+lws_uv_initvhost(struct lws_vhost* vh, struct lws*);
 #define LWS_LIBUV_ENABLED(context) lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)
 LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info);
 #else
index f7e6fb4..d4a9d16 100644 (file)
@@ -153,6 +153,12 @@ lws_context_init_server(struct lws_context_creation_info *info,
        wsi->listener = 1;
 
        vhost->context->pt[m].wsi_listening = wsi;
+
+#ifdef LWS_USE_LIBUV
+       if (LWS_LIBUV_ENABLED(vhost->context))
+               lws_uv_initvhost(vhost, wsi);
+#endif
+
        if (insert_wsi_socket_into_fds(vhost->context, wsi))
                goto bail;