Skip to content
Snippets Groups Projects
context.c 38.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	lwsl_info(" mem: http hdr rsvd:   %5lu bytes (%u thr x (%u + %lu) x %u))\n",
    		    (long)(context->max_http_header_data +
    
    Andy Green's avatar
    Andy Green committed
    		     sizeof(struct allocated_headers)) *
    
    Andy Green's avatar
    Andy Green committed
    		    context->max_http_header_pool * context->count_threads,
    		    context->count_threads,
    
    Andy Green's avatar
    Andy Green committed
    		    context->max_http_header_data,
    
    		    (long)sizeof(struct allocated_headers),
    
    		    context->max_http_header_pool);
    
    Andy Green's avatar
    Andy Green committed
    	n = sizeof(struct lws_pollfd) * context->count_threads *
    	    context->fd_limit_per_thread;
    
    Andy Green's avatar
    Andy Green committed
    	context->pt[0].fds = lws_zalloc(n, "fds table");
    
    Andy Green's avatar
    Andy Green committed
    	if (context->pt[0].fds == NULL) {
    
    		lwsl_err("OOM allocating %d fds\n", context->max_fds);
    
    		goto bail;
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" mem: pollfd map:      %5u\n", n);
    
    	if (info->server_string) {
    		context->server_string = info->server_string;
    
    		context->server_string_len = (short)
    				strlen(context->server_string);
    
    Andy Green's avatar
    Andy Green committed
    #if LWS_MAX_SMP > 1
    
    Andy Green's avatar
    Andy Green committed
    	/* each thread serves his own chunk of fds */
    	for (n = 1; n < (int)info->count_threads; n++)
    
    Andy Green's avatar
    Andy Green committed
    		context->pt[n].fds = context->pt[n - 1].fds +
    				     context->fd_limit_per_thread;
    #endif
    
    	if (lws_plat_init(context, info))
    
    		goto bail;
    
    Andy Green's avatar
    Andy Green committed
    	lws_context_init_ssl_library(info);
    
    
    Andy Green's avatar
    Andy Green committed
    	context->user_space = info->user;
    
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * if he's not saying he'll make his own vhosts later then act
    	 * compatibly and make a default vhost using the data in the info
    	 */
    	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
    
    		if (!lws_create_vhost(context, info)) {
    
    Andy Green's avatar
    Andy Green committed
    			lwsl_err("Failed to create default vhost\n");
    			return NULL;
    		}
    
    
    	lws_context_init_extensions(info, context);
    
    
    	lwsl_info(" mem: per-conn:        %5lu bytes + protocol rx buf\n",
    
    	strcpy(context->canonical_hostname, "unknown");
    
    	lws_server_get_canonical_hostname(context, info);
    
    Andy Green's avatar
    Andy Green committed
    	context->uid = info->uid;
    	context->gid = info->gid;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
    	memcpy(context->caps, info->caps, sizeof(context->caps));
    	context->count_caps = info->count_caps;
    #endif
    
    
    	/*
    	 * drop any root privs for this process
    	 * to listen on port < 1023 we would have needed root, but now we are
    	 * listening, we don't want the power for anything else
    	 */
    
    Andy Green's avatar
    Andy Green committed
    	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
    		lws_plat_drop_app_privileges(info);
    
    
    	/*
    	 * give all extensions a chance to create any per-context
    	 * allocations they need
    	 */
    	if (info->port != CONTEXT_PORT_NO_LISTEN) {
    
    Andy Green's avatar
    Andy Green committed
    		if (lws_ext_cb_all_exts(context, NULL,
    
    			LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
    
    Andy Green's avatar
    Andy Green committed
    		if (lws_ext_cb_all_exts(context, NULL,
    
    			LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
    
    	lws_context_destroy(context);
    
    Andy Green's avatar
    Andy Green committed
    LWS_VISIBLE LWS_EXTERN void
    lws_context_deprecate(struct lws_context *context, lws_reload_func cb)
    {
    	struct lws_vhost *vh = context->vhost_list, *vh1;
    	struct lws *wsi;
    
    	/*
    	 * "deprecation" means disable the context from accepting any new
    	 * connections and free up listen sockets to be used by a replacement
    	 * context.
    	 *
    	 * Otherwise the deprecated context remains operational, until its
    	 * number of connected sockets falls to zero, when it is deleted.
    	 */
    
    	/* for each vhost, close his listen socket */
    
    	while (vh) {
    		wsi = vh->lserv_wsi;
    		if (wsi) {
    			wsi->socket_is_permanently_unusable = 1;
    			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
    			wsi->context->deprecation_pending_listen_close_count++;
    			/*
    			 * other vhosts can share the listen port, they
    			 * point to the same wsi.  So zap those too.
    			 */
    			vh1 = context->vhost_list;
    			while (vh1) {
    				if (vh1->lserv_wsi == wsi)
    					vh1->lserv_wsi = NULL;
    				vh1 = vh1->vhost_next;
    			}
    		}
    		vh = vh->vhost_next;
    	}
    
    	context->deprecated = 1;
    	context->deprecation_cb = cb;
    }
    
    LWS_VISIBLE LWS_EXTERN int
    lws_context_is_deprecated(struct lws_context *context)
    {
    	return context->deprecated;
    }
    
    LWS_VISIBLE void
    lws_context_destroy2(struct lws_context *context);
    
    
    Andy Green's avatar
    Andy Green committed
    
    static void
    lws_vhost_destroy1(struct lws_vhost *vh)
    {
    	const struct lws_protocols *protocol = NULL;
    	struct lws_context_per_thread *pt;
    	int n, m = vh->context->count_threads;
    	struct lws_context *context = vh->context;
    	struct lws wsi;
    
    
    	lwsl_info("%s\n", __func__);
    
    Andy Green's avatar
    Andy Green committed
    
    	if (vh->being_destroyed)
    		return;
    
    	vh->being_destroyed = 1;
    
    	/*
    	 * Are there other vhosts that are piggybacking on our listen socket?
    	 * If so we need to hand the listen socket off to one of the others
    	 * so it will remain open.  If not, leave it attached to the closing
    	 * vhost and it will get closed.
    	 */
    
    	if (vh->lserv_wsi)
    		lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
    			if (v != vh &&
    			    !v->being_destroyed &&
    			    v->listen_port == vh->listen_port &&
    			    ((!v->iface && !vh->iface) ||
    			    (v->iface && vh->iface &&
    			    !strcmp(v->iface, vh->iface)))) {
    				/*
    				 * this can only be a listen wsi, which is
    				 * restricted... it has no protocol or other
    				 * bindings or states.  So we can simply
    				 * swap it to a vhost that has the same
    				 * iface + port, but is not closing.
    				 */
    				assert(v->lserv_wsi == NULL);
    				v->lserv_wsi = vh->lserv_wsi;
    				vh->lserv_wsi = NULL;
    				v->lserv_wsi->vhost = v;
    
    				lwsl_notice("%s: listen skt from %s to %s\n",
    					    __func__, vh->name, v->name);
    				break;
    			}
    		} lws_end_foreach_ll(v, vhost_next);
    
    	/*
    	 * Forcibly close every wsi assoicated with this vhost.  That will
    	 * include the listen socket if it is still associated with the closing
    	 * vhost.
    	 */
    
    	while (m--) {
    		pt = &context->pt[m];
    
    		for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
    			struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
    			if (!wsi)
    				continue;
    			if (wsi->vhost != vh)
    				continue;
    
    			lws_close_free_wsi(wsi,
    				LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
    				/* no protocol close */);
    			n--;
    		}
    	}
    
    	/*
    	 * let the protocols destroy the per-vhost protocol objects
    	 */
    
    	memset(&wsi, 0, sizeof(wsi));
    	wsi.context = vh->context;
    	wsi.vhost = vh;
    	protocol = vh->protocols;
    	if (protocol) {
    		n = 0;
    		while (n < vh->count_protocols) {
    			wsi.protocol = protocol;
    			protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
    					   NULL, NULL, 0);
    			protocol++;
    			n++;
    		}
    	}
    
    	/*
    	 * remove vhost from context list of vhosts
    	 */
    
    	lws_start_foreach_llp(struct lws_vhost **, pv, context->vhost_list) {
    		if (*pv == vh) {
    			*pv = vh->vhost_next;
    			break;
    		}
    	} lws_end_foreach_llp(pv, vhost_next);
    
    	/* add ourselves to the pending destruction list */
    
    	vh->vhost_next = vh->context->vhost_pending_destruction_list;
    	vh->context->vhost_pending_destruction_list = vh;
    }
    
    static void
    lws_vhost_destroy2(struct lws_vhost *vh)
    {
    	const struct lws_protocols *protocol = NULL;
    	struct lws_context *context = vh->context;
    	struct lws_deferred_free *df;
    	int n;
    
    
    	lwsl_info("%s: %p\n", __func__, vh);
    
    Andy Green's avatar
    Andy Green committed
    
    	/* if we are still on deferred free list, remove ourselves */
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_start_foreach_llp(struct lws_deferred_free **, pdf,
    			      context->deferred_free_list) {
    
    Andy Green's avatar
    Andy Green committed
    		if ((*pdf)->payload == vh) {
    			df = *pdf;
    			*pdf = df->next;
    			lws_free(df);
    			break;
    		}
    	} lws_end_foreach_llp(pdf, next);
    
    	/* remove ourselves from the pending destruction list */
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_start_foreach_llp(struct lws_vhost **, pv,
    			      context->vhost_pending_destruction_list) {
    
    Andy Green's avatar
    Andy Green committed
    		if ((*pv) == vh) {
    			*pv = (*pv)->vhost_next;
    			break;
    		}
    	} lws_end_foreach_llp(pv, vhost_next);
    
    	/*
    	 * Free all the allocations associated with the vhost
    	 */
    
    	protocol = vh->protocols;
    	if (protocol) {
    		n = 0;
    		while (n < vh->count_protocols) {
    			if (vh->protocol_vh_privs &&
    			    vh->protocol_vh_privs[n]) {
    				lws_free(vh->protocol_vh_privs[n]);
    				vh->protocol_vh_privs[n] = NULL;
    			}
    			protocol++;
    			n++;
    		}
    	}
    	if (vh->protocol_vh_privs)
    		lws_free(vh->protocol_vh_privs);
    	lws_ssl_SSL_CTX_destroy(vh);
    	lws_free(vh->same_vh_protocol_list);
    #ifdef LWS_WITH_PLUGINS
    	if (LWS_LIBUV_ENABLED(context)) {
    		if (context->plugin_list)
    			lws_free((void *)vh->protocols);
    	} else
    #endif
    	{
    		if (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)
    			lws_free((void *)vh->protocols);
    	}
    
    #ifdef LWS_WITH_PLUGINS
    #ifndef LWS_NO_EXTENSIONS
    	if (context->plugin_extension_count)
    		lws_free((void *)vh->extensions);
    #endif
    #endif
    #ifdef LWS_WITH_ACCESS_LOG
    	if (vh->log_fd != (int)LWS_INVALID_FILE)
    		close(vh->log_fd);
    #endif
    
    	/*
    	 * although async event callbacks may still come for wsi handles with
    	 * pending close in the case of asycn event library like libuv,
    	 * they do not refer to the vhost.  So it's safe to free.
    	 */
    
    
    	lwsl_info("  %s: Freeing vhost %p\n", __func__, vh);
    
    Andy Green's avatar
    Andy Green committed
    
    	memset(vh, 0, sizeof(*vh));
    
    	lws_free(vh);
    
    Andy Green's avatar
    Andy Green committed
    }
    
    int
    lws_check_deferred_free(struct lws_context *context, int force)
    {
    	struct lws_deferred_free *df;
    	time_t now = lws_now_secs();
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_start_foreach_llp(struct lws_deferred_free **, pdf,
    			      context->deferred_free_list) {
    
    Andy Green's avatar
    Andy Green committed
    		if (now > (*pdf)->deadline || force) {
    			df = *pdf;
    			*pdf = df->next;
    			/* finalize vh destruction */
    
    Andy Green's avatar
    Andy Green committed
    			lwsl_notice("deferred vh %p destroy\n", df->payload);
    
    Andy Green's avatar
    Andy Green committed
    			lws_vhost_destroy2(df->payload);
    			lws_free(df);
    			continue; /* after deletion we already point to next */
    		}
    	} lws_end_foreach_llp(pdf, next);
    
    	return 0;
    }
    
    LWS_VISIBLE void
    lws_vhost_destroy(struct lws_vhost *vh)
    {
    
    Andy Green's avatar
    Andy Green committed
    	struct lws_deferred_free *df = lws_malloc(sizeof(*df), "deferred free");
    
    Andy Green's avatar
    Andy Green committed
    
    	if (!df)
    		return;
    
    	lws_vhost_destroy1(vh);
    
    	/* part 2 is deferred to allow all the handle closes to complete */
    
    	df->next = vh->context->deferred_free_list;
    	df->deadline = lws_now_secs() + 5;
    	df->payload = vh;
    	vh->context->deferred_free_list = df;
    }
    
    
    lws_context_destroy(struct lws_context *context)
    
    Andy Green's avatar
    Andy Green committed
    	struct lws_context_per_thread *pt;
    
    Andy Green's avatar
    Andy Green committed
    	struct lws_vhost *vh = NULL;
    
    Andy Green's avatar
    Andy Green committed
    	if (!context) {
    		lwsl_notice("%s: ctx %p\n", __func__, context);
    		return;
    	}
    	if (context->being_destroyed1) {
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_notice("%s: ctx %p: already being destroyed\n",
    			    __func__, context);
    
    	lwsl_info("%s: ctx %p\n", __func__, context);
    
    	context->being_destroyed = 1;
    
    Andy Green's avatar
    Andy Green committed
    	context->being_destroyed1 = 1;
    
    	memset(&wsi, 0, sizeof(wsi));
    	wsi.context = context;
    
    
    #ifdef LWS_LATENCY
    	if (context->worst_latency_info[0])
    		lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    	while (m--) {
    		pt = &context->pt[m];
    
    
    Andy Green's avatar
    Andy Green committed
    		for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
    
    Andy Green's avatar
    Andy Green committed
    			struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
    
    Andy Green's avatar
    Andy Green committed
    			if (!wsi)
    				continue;
    
    Andy Green's avatar
    Andy Green committed
    
    			lws_close_free_wsi(wsi,
    				LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
    
    				/* no protocol close */);
    
    Andy Green's avatar
    Andy Green committed
    			n--;
    		}
    
    		lws_pt_mutex_destroy(pt);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    Andy Green's avatar
    Andy Green committed
    
    
    	/*
    	 * give all extensions a chance to clean up any per-context
    	 * allocations they might have made
    	 */
    
    Andy Green's avatar
    Andy Green committed
    	n = lws_ext_cb_all_exts(context, NULL,
    
    Andy Green's avatar
    Andy Green committed
    				LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
    
    Andy Green's avatar
    Andy Green committed
    	n = lws_ext_cb_all_exts(context, NULL,
    
    Andy Green's avatar
    Andy Green committed
    				LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
    
    
    	/*
    	 * inform all the protocols that they are done and will have no more
    
    Andy Green's avatar
    Andy Green committed
    	 * callbacks.
    	 *
    	 * We can't free things until after the event loop shuts down.
    
    	if (context->protocol_init_done)
    		vh = context->vhost_list;
    
    Andy Green's avatar
    Andy Green committed
    	while (vh) {
    
    Andy Green's avatar
    Andy Green committed
    		lws_vhost_destroy1(vh);
    
    Andy Green's avatar
    Andy Green committed
    		vh = vh->vhost_next;
    	}
    
    Andy Green's avatar
    Andy Green committed
    	for (n = 0; n < context->count_threads; n++) {
    
    Andy Green's avatar
    Andy Green committed
    		pt = &context->pt[n];
    
    		lws_libev_destroyloop(context, n);
    		lws_libuv_destroyloop(context, n);
    
    		lws_libevent_destroyloop(context, n);
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    		lws_free_set_NULL(context->pt[n].serv_buf);
    
    Andy Green's avatar
    Andy Green committed
    		if (pt->ah_pool)
    			lws_free(pt->ah_pool);
    		if (pt->http_header_data)
    			lws_free(pt->http_header_data);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    	lws_plat_context_early_destroy(context);
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    	if (context->pt[0].fds)
    
    Andy Green's avatar
    Andy Green committed
    		lws_free_set_NULL(context->pt[0].fds);
    
    Andy Green's avatar
    Andy Green committed
    	if (!LWS_LIBUV_ENABLED(context))
    		lws_context_destroy2(context);
    }
    
    /*
     * call the second one after the event loop has been shut down cleanly
     */
    
    LWS_VISIBLE void
    lws_context_destroy2(struct lws_context *context)
    {
    	struct lws_vhost *vh = NULL, *vh1;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_PEER_LIMITS)
    	uint32_t n;
    #endif
    
    Andy Green's avatar
    Andy Green committed
    
    
    	lwsl_info("%s: ctx %p\n", __func__, context);
    
    Andy Green's avatar
    Andy Green committed
    
    	/*
    	 * free all the per-vhost allocations
    	 */
    
    Andy Green's avatar
    Andy Green committed
    
    	vh = context->vhost_list;
    	while (vh) {
    		vh1 = vh->vhost_next;
    
    Andy Green's avatar
    Andy Green committed
    		lws_vhost_destroy2(vh);
    
    Andy Green's avatar
    Andy Green committed
    		vh = vh1;
    	}
    
    
    Andy Green's avatar
    Andy Green committed
    	/* remove ourselves from the pending destruction list */
    
    	while (context->vhost_pending_destruction_list)
    		/* removes itself from list */
    		lws_vhost_destroy2(context->vhost_pending_destruction_list);
    
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_stats_log_dump(context);
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_ssl_context_destroy(context);
    
    	lws_plat_context_late_destroy(context);
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_PEER_LIMITS)
    	for (n = 0; n < context->pl_hash_elements; n++)	{
    
    Andy Green's avatar
    Andy Green committed
    		lws_start_foreach_llp(struct lws_peer **, peer,
    				      context->pl_hash_table[n]) {
    
    Andy Green's avatar
    Andy Green committed
    			struct lws_peer *df = *peer;
    			*peer = df->next;
    			lws_free(df);
    			continue;
    		} lws_end_foreach_llp(peer, next);
    	}
    	lws_free(context->pl_hash_table);
    #endif
    
    
    	if (context->external_baggage_free_on_destroy)
    		free(context->external_baggage_free_on_destroy);
    
    
    Andy Green's avatar
    Andy Green committed
    	lws_check_deferred_free(context, 1);
    
    
    Andy Green's avatar
    Andy Green committed
    #if LWS_MAX_SMP > 1
    	pthread_mutex_destroy(&context->lock, NULL);
    #endif
    
    
    	lws_free(context);