Skip to content
Snippets Groups Projects
context.c 45.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		lwsl_info("IPV6 compiled in and enabled\n");
    
    		lwsl_info("IPV6 compiled in but disabled\n");
    
    	lwsl_info("IPV6 not compiled in\n");
    
    Andy Green's avatar
    Andy Green committed
    #if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_ESP32)
    
    	lws_feature_status_libev(info);
    
    Andy Green's avatar
    Andy Green committed
    	lws_feature_status_libuv(info);
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" LWS_DEF_HEADER_LEN    : %u\n", LWS_DEF_HEADER_LEN);
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" LWS_MAX_PROTOCOLS     : %u\n", LWS_MAX_PROTOCOLS);
    	lwsl_info(" LWS_MAX_SMP           : %u\n", LWS_MAX_SMP);
    
    	lwsl_info(" sizeof (*info)        : %ld\n", (long)sizeof(*info));
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_STATS)
    
    	lwsl_info(" LWS_WITH_STATS        : on\n");
    
    Andy Green's avatar
    Andy Green committed
    #endif
    
    	lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP2)
    	lwsl_info(" HTTP2 support         : available\n");
    #else
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" HTTP2 support         : not configured\n");
    
    	if (lws_plat_context_early_init())
    		return NULL;
    
    
    Andy Green's avatar
    Andy Green committed
    	context = lws_zalloc(sizeof(struct lws_context), "context");
    
    	if (!context) {
    		lwsl_err("No memory for websocket context\n");
    		return NULL;
    	}
    
    	if (info->pt_serv_buf_size)
    		context->pt_serv_buf_size = info->pt_serv_buf_size;
    	else
    		context->pt_serv_buf_size = 4096;
    
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_ROLE_H2)
    	role_ops_h2.init_context(context, info);
    
    Andy Green's avatar
    Andy Green committed
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    #if LWS_MAX_SMP > 1
    	pthread_mutex_init(&context->lock, NULL);
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_ESP32)
    	context->last_free_heap = esp_get_free_heap_size();
    #endif
    
    
    	/* default to just the platform fops implementation */
    
    
    	context->fops_platform.LWS_FOP_OPEN	= _lws_plat_file_open;
    	context->fops_platform.LWS_FOP_CLOSE	= _lws_plat_file_close;
    	context->fops_platform.LWS_FOP_SEEK_CUR	= _lws_plat_file_seek_cur;
    	context->fops_platform.LWS_FOP_READ	= _lws_plat_file_read;
    	context->fops_platform.LWS_FOP_WRITE	= _lws_plat_file_write;
    
    Andy Green's avatar
    Andy Green committed
    	context->fops_platform.fi[0].sig	= NULL;
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 *  arrange a linear linked-list of fops starting from context->fops
    	 *
    	 * platform fops
    	 * [ -> fops_zip (copied into context so .next settable) ]
    	 * [ -> info->fops ]
    	 */
    
    	context->fops = &context->fops_platform;
    	prev = (struct lws_plat_file_ops *)context->fops;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_ZIP_FOPS)
    	/* make a soft copy so we can set .next */
    	context->fops_zip = fops_zip;
    	prev->next = &context->fops_zip;
    	prev = (struct lws_plat_file_ops *)prev->next;
    #endif
    
    	/* if user provided fops, tack them on the end of the list */
    
    Andy Green's avatar
    Andy Green committed
    		prev->next = info->fops;
    
    	context->reject_service_keywords = info->reject_service_keywords;
    
    	if (info->external_baggage_free_on_destroy)
    		context->external_baggage_free_on_destroy =
    			info->external_baggage_free_on_destroy;
    
    Andy Green's avatar
    Andy Green committed
    	context->time_up = time(NULL);
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    	context->simultaneous_ssl_restriction =
    			info->simultaneous_ssl_restriction;
    
    #ifndef LWS_NO_DAEMONIZE
    
    	if (pid_daemon) {
    		context->started_with_parent = pid_daemon;
    
    		lwsl_info(" Started with daemon pid %d\n", pid_daemon);
    
    Andy Green's avatar
    Andy Green committed
    #endif
    
    Galen Ma's avatar
    Galen Ma committed
    #if defined(__ANDROID__)
    		n = getrlimit ( RLIMIT_NOFILE,&rt);
    		if (-1 == n) {
    			lwsl_err("Get RLIMIT_NOFILE failed!\n");
    			return NULL;
    		}
    		context->max_fds = rt.rlim_cur;
    #else
    		context->max_fds = getdtablesize();
    #endif
    
    Andy Green's avatar
    Andy Green committed
    
    	if (info->count_threads)
    		context->count_threads = info->count_threads;
    	else
    		context->count_threads = 1;
    
    	if (context->count_threads > LWS_MAX_SMP)
    		context->count_threads = LWS_MAX_SMP;
    
    
    	context->token_limits = info->token_limits;
    
    Andy Green's avatar
    Andy Green committed
    
    
    	context->options = info->options;
    
    Andy Green's avatar
    Andy Green committed
    
    
    #if defined(LWS_WITH_TLS)
    	if (info->alpn)
    		context->alpn_default = info->alpn;
    	else {
    		char *p = alpn_discovered, first = 1;
    
    		for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++) {
    			if (available_roles[n]->alpn) {
    				if (!first)
    					*p++ = ',';
    				p += lws_snprintf(p, alpn_discovered +
    						sizeof(alpn_discovered) - 2 - p,
    					    "%s", available_roles[n]->alpn);
    				first = 0;
    			}
    		}
    		context->alpn_default = alpn_discovered;
    	}
    
    	lwsl_info("Default ALPN advertisment: %s\n", context->alpn_default);
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    	if (info->timeout_secs)
    		context->timeout_secs = info->timeout_secs;
    	else
    		context->timeout_secs = AWAITING_TIMEOUT;
    
    
    	context->ws_ping_pong_interval = info->ws_ping_pong_interval;
    
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" default timeout (secs): %u\n", context->timeout_secs);
    
    Andy Green's avatar
    Andy Green committed
    	if (info->max_http_header_data)
    		context->max_http_header_data = info->max_http_header_data;
    	else
    
    		if (info->max_http_header_data2)
    			context->max_http_header_data =
    					info->max_http_header_data2;
    		else
    			context->max_http_header_data = LWS_DEF_HEADER_LEN;
    
    Andy Green's avatar
    Andy Green committed
    	if (info->max_http_header_pool)
    		context->max_http_header_pool = info->max_http_header_pool;
    	else
    		context->max_http_header_pool = LWS_DEF_HEADER_POOL;
    
    	/*
    	 * Allocate the per-thread storage for scratchpad buffers,
    	 * and header data pool
    
    Andy Green's avatar
    Andy Green committed
    	 */
    	for (n = 0; n < context->count_threads; n++) {
    
    		context->pt[n].serv_buf = lws_malloc(context->pt_serv_buf_size,
    						     "pt_serv_buf");
    
    Andy Green's avatar
    Andy Green committed
    		if (!context->pt[n].serv_buf) {
    			lwsl_err("OOM\n");
    			return NULL;
    		}
    
    #ifdef LWS_WITH_LIBUV
    
    		context->pt[n].context = context;
    
    		context->pt[n].tid = n;
    
    		context->pt[n].ah_list = NULL;
    		context->pt[n].ah_pool_length = 0;
    
    Andy Green's avatar
    Andy Green committed
    
    		lws_pt_mutex_init(&context->pt[n]);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    	if (info->fd_limit_per_thread)
    		context->fd_limit_per_thread = info->fd_limit_per_thread;
    	else
    		context->fd_limit_per_thread = context->max_fds /
    					       context->count_threads;
    
    
    	lwsl_info(" Threads: %d each %d fds\n", context->count_threads,
    
    Andy Green's avatar
    Andy Green committed
    		    context->fd_limit_per_thread);
    
    
    	if (!info->ka_interval && info->ka_time > 0) {
    		lwsl_err("info->ka_interval can't be 0 if ka_time used\n");
    		return NULL;
    	}
    
    #ifdef LWS_WITH_LIBEV
    
    	/* (Issue #264) In order to *avoid breaking backwards compatibility*, we
    	 * enable libev mediated SIGINT handling with a default handler of
    
    	 * lws_sigint_cb. The handler can be overridden or disabled
    	 * by invoking lws_sigint_cfg after creating the context, but
    	 * before invoking lws_initloop:
    
    	 */
    	context->use_ev_sigint = 1;
    
    Andy Green's avatar
    Andy Green committed
    	context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
    
    #endif /* LWS_WITH_LIBEV */
    #ifdef LWS_WITH_LIBUV
    
    Andy Green's avatar
    Andy Green committed
    	/* (Issue #264) In order to *avoid breaking backwards compatibility*, we
    	 * enable libev mediated SIGINT handling with a default handler of
    	 * lws_sigint_cb. The handler can be overridden or disabled
    	 * by invoking lws_sigint_cfg after creating the context, but
    	 * before invoking lws_initloop:
    	 */
    	context->use_ev_sigint = 1;
    	context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
    #endif
    
    #ifdef LWS_WITH_LIBEVENT
    
    	/* (Issue #264) In order to *avoid breaking backwards compatibility*, we
    	 * enable libev mediated SIGINT handling with a default handler of
    	 * lws_sigint_cb. The handler can be overridden or disabled
    	 * by invoking lws_sigint_cfg after creating the context, but
    	 * before invoking lws_initloop:
    	 */
    	context->use_ev_sigint = 1;
    	context->lws_event_sigint_cb = &lws_event_sigint_cb;
    
    #endif /* LWS_WITH_LIBEVENT */
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_PEER_LIMITS)
    	/* scale the peer hash table according to the max fds for the process,
    	 * so that the max list depth averages 16.  Eg, 1024 fd -> 64,
    	 * 102400 fd -> 6400
    	 */
    	context->pl_hash_elements =
    		(context->count_threads * context->fd_limit_per_thread) / 16;
    	context->pl_hash_table = lws_zalloc(sizeof(struct lws_peer *) *
    
    Andy Green's avatar
    Andy Green committed
    			context->pl_hash_elements, "peer limits hash table");
    
    Andy Green's avatar
    Andy Green committed
    	context->ip_limit_ah = info->ip_limit_ah;
    	context->ip_limit_wsi = info->ip_limit_wsi;
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" mem: context:         %5lu B (%ld ctx + (%ld thr x %d))\n",
    
    		  (long)sizeof(struct lws_context) +
    
    		  (context->count_threads * context->pt_serv_buf_size),
    
    		  (long)sizeof(struct lws_context),
    		  (long)context->count_threads,
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_info(" mem: http hdr rsvd:   %5lu B (%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
    #if defined(LWS_WITH_HTTP2)
    	/*
    	 * let the user code see what the platform default SETTINGS were, he
    	 * can modify them when he creates the vhosts.
    	 */
    	for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
    		info->http2_settings[n] = context->set.s[n];
    #endif
    
    
    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");
    
    			for (n = 0; n < context->count_threads; n++)
    				lws_free_set_NULL(context->pt[n].serv_buf);
    #if defined(LWS_WITH_PEER_LIMITS)
    			lws_free_set_NULL(context->pl_hash_table);
    #endif
    			lws_free_set_NULL(context->pt[0].fds);
    			lws_plat_context_late_destroy(context);
    			lws_free_set_NULL(context);
    
    Andy Green's avatar
    Andy Green committed
    			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
    
    
    	/*
    	 * The event libs handle doing this when their event loop starts,
    	 * if we are using the default poll() service, do it here
    	 */
    
    	if (!LWS_LIBEV_ENABLED(context) &&
    	    !LWS_LIBUV_ENABLED(context) &&
    	    !LWS_LIBEVENT_ENABLED(context) && lws_create_event_pipes(context))
    		goto bail;
    
    
    	/*
    	 * 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)
    
    	time(&context->last_cert_check_s);
    
    
    #if defined(LWS_WITH_SELFTESTS)
    	lws_jws_selftest();
    #endif
    
    
    	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, "ctx deprecate");
    
    Andy Green's avatar
    Andy Green committed
    			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
    
    
    Andy Green's avatar
    Andy Green committed
    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)
    
    Andy Green's avatar
    Andy Green committed
    		lws_start_foreach_ll(struct lws_vhost *, v,
    				     context->vhost_list) {
    
    Andy Green's avatar
    Andy Green committed
    			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;
    
    				if (v->lserv_wsi)
    					v->lserv_wsi->vhost = v;
    
    Andy Green's avatar
    Andy Green committed
    
    				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,
    				"vh destroy"
    
    Andy Green's avatar
    Andy Green committed
    				/* no protocol close */);
    			n--;
    		}
    	}
    
    
    	/*
    	 * destroy any pending timed events
    	 */
    
    	while (vh->timed_vh_protocol_list)
    		lws_timed_callback_remove(vh, vh->timed_vh_protocol_list);
    
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * 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 && vh->created_vhost_protocols) {
    
    Andy Green's avatar
    Andy Green committed
    		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
    
    #if !defined(LWS_WITHOUT_EXTENSIONS)
    
    Andy Green's avatar
    Andy Green committed
    	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
    
    
    	lws_free_set_NULL(vh->alloc_cert_path);
    
    
    Andy Green's avatar
    Andy Green committed
    #if LWS_MAX_SMP > 1
           pthread_mutex_destroy(&vh->lock);
    #endif
    
    
    #if defined(LWS_WITH_UNIX_SOCK)
    	if (LWS_UNIX_SOCK_ENABLED(context)) {
    		n = unlink(vh->iface);
    		if (n)
    			lwsl_info("Closing unix socket %s: errno %d\n",
    				  vh->iface, errno);
    	}
    #endif
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * 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) {
    
    		if (force ||
    		    lws_compare_time_t(context, now, (*pdf)->deadline) > 5) {
    
    Andy Green's avatar
    Andy Green committed
    			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();
    
    Andy Green's avatar
    Andy Green committed
    	df->payload = vh;
    	vh->context->deferred_free_list = df;
    }
    
    
    lws_context_destroy(struct lws_context *context)
    
    	volatile struct lws_foreign_thread_pollfd *ftp, *next;
    	volatile struct lws_context_per_thread *vpt;
    
    	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];
    
    		vpt = (volatile struct lws_context_per_thread *)pt;
    
    		ftp = vpt->foreign_pfd_list;
    		while (ftp) {
    			next = ftp->next;
    			lws_free((void *)ftp);
    			ftp = next;
    		}
    		vpt->foreign_pfd_list = NULL;
    
    Andy Green's avatar
    Andy Green committed
    
    
    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
    
    
    Sungtae Kim's avatar
    Sungtae Kim committed
    			if (wsi->event_pipe)
    				lws_destroy_event_pipe(wsi);
    			else
    
    					LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY,
    					"ctx destroy"
    
    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) {
    
    		struct lws_vhost *vhn = vh->vhost_next;
    
    Andy Green's avatar
    Andy Green committed
    		lws_vhost_destroy1(vh);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    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);
    
    
    		while (pt->ah_list)
    			_lws_destroy_ah(pt, pt->ah_list);
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    	lws_plat_context_early_destroy(context);
    
    Andy Green's avatar
    Andy Green committed
    
    
    #if defined(LWS_WITH_LIBUV)
    	if (LWS_LIBUV_ENABLED(context))
    		for (n = 0; n < context->count_threads; n++) {
    			pt = &context->pt[n];
    			if (!pt->ev_loop_foreign) {
    #if UV_VERSION_MAJOR > 0
    				uv_loop_close(pt->io_loop_uv);
    #endif
    				lws_free_set_NULL(pt->io_loop_uv);
    			}
    		}
    #endif
    
    
    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);
    
    Andy Green's avatar
    Andy Green committed
    #endif
    
    
    	lws_free(context);