Beispiel #1
0
/***
 *** Task Manager.
 ***/
static void
dispatch(isc_taskmgr_t *manager) {
	isc_task_t *task;
#ifndef ISC_PLATFORM_USETHREADS
	unsigned int total_dispatch_count = 0;
	isc_tasklist_t ready_tasks;
#endif /* ISC_PLATFORM_USETHREADS */

	REQUIRE(VALID_MANAGER(manager));

	/*
	 * Again we're trying to hold the lock for as short a time as possible
	 * and to do as little locking and unlocking as possible.
	 *
	 * In both while loops, the appropriate lock must be held before the
	 * while body starts.  Code which acquired the lock at the top of
	 * the loop would be more readable, but would result in a lot of
	 * extra locking.  Compare:
	 *
	 * Straightforward:
	 *
	 *	LOCK();
	 *	...
	 *	UNLOCK();
	 *	while (expression) {
	 *		LOCK();
	 *		...
	 *		UNLOCK();
	 *
	 *	       	Unlocked part here...
	 *
	 *		LOCK();
	 *		...
	 *		UNLOCK();
	 *	}
	 *
	 * Note how if the loop continues we unlock and then immediately lock.
	 * For N iterations of the loop, this code does 2N+1 locks and 2N+1
	 * unlocks.  Also note that the lock is not held when the while
	 * condition is tested, which may or may not be important, depending
	 * on the expression.
	 *
	 * As written:
	 *
	 *	LOCK();
	 *	while (expression) {
	 *		...
	 *		UNLOCK();
	 *
	 *	       	Unlocked part here...
	 *
	 *		LOCK();
	 *		...
	 *	}
	 *	UNLOCK();
	 *
	 * For N iterations of the loop, this code does N+1 locks and N+1
	 * unlocks.  The while expression is always protected by the lock.
	 */

#ifndef ISC_PLATFORM_USETHREADS
	ISC_LIST_INIT(ready_tasks);
#endif
	LOCK(&manager->lock);
	while (!FINISHED(manager)) {
#ifdef ISC_PLATFORM_USETHREADS
		/*
		 * For reasons similar to those given in the comment in
		 * isc_task_send() above, it is safe for us to dequeue
		 * the task while only holding the manager lock, and then
		 * change the task to running state while only holding the
		 * task lock.
		 */
		while ((EMPTY(manager->ready_tasks) ||
			manager->exclusive_requested) &&
			!FINISHED(manager))
		{
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_GENERAL,
						    ISC_MSG_WAIT, "wait"));
			WAIT(&manager->work_available, &manager->lock);
			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
						    ISC_MSGSET_TASK,
						    ISC_MSG_AWAKE, "awake"));
		}
#else /* ISC_PLATFORM_USETHREADS */
		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
		    EMPTY(manager->ready_tasks))
			break;
#endif /* ISC_PLATFORM_USETHREADS */
		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
					    ISC_MSG_WORKING, "working"));

		task = HEAD(manager->ready_tasks);
		if (task != NULL) {
			unsigned int dispatch_count = 0;
			isc_boolean_t done = ISC_FALSE;
			isc_boolean_t requeue = ISC_FALSE;
			isc_boolean_t finished = ISC_FALSE;
			isc_event_t *event;

			INSIST(VALID_TASK(task));

			/*
			 * Note we only unlock the manager lock if we actually
			 * have a task to do.  We must reacquire the manager
			 * lock before exiting the 'if (task != NULL)' block.
			 */
			DEQUEUE(manager->ready_tasks, task, ready_link);
			manager->tasks_running++;
			UNLOCK(&manager->lock);

			LOCK(&task->lock);
			INSIST(task->state == task_state_ready);
			task->state = task_state_running;
			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
					      ISC_MSG_RUNNING, "running"));
			isc_stdtime_get(&task->now);
			do {
				if (!EMPTY(task->events)) {
					event = HEAD(task->events);
					DEQUEUE(task->events, event, ev_link);

					/*
					 * Execute the event action.
					 */
					XTRACE(isc_msgcat_get(isc_msgcat,
							    ISC_MSGSET_TASK,
							    ISC_MSG_EXECUTE,
							    "execute action"));
					if (event->ev_action != NULL) {
						UNLOCK(&task->lock);
						(event->ev_action)(task,event);
						LOCK(&task->lock);
					}
					dispatch_count++;
#ifndef ISC_PLATFORM_USETHREADS
					total_dispatch_count++;
#endif /* ISC_PLATFORM_USETHREADS */
				}

				if (task->references == 0 &&
				    EMPTY(task->events) &&
				    !TASK_SHUTTINGDOWN(task)) {
					isc_boolean_t was_idle;

					/*
					 * There are no references and no
					 * pending events for this task,
					 * which means it will not become
					 * runnable again via an external
					 * action (such as sending an event
					 * or detaching).
					 *
					 * We initiate shutdown to prevent
					 * it from becoming a zombie.
					 *
					 * We do this here instead of in
					 * the "if EMPTY(task->events)" block
					 * below because:
					 *
					 *	If we post no shutdown events,
					 *	we want the task to finish.
					 *
					 *	If we did post shutdown events,
					 *	will still want the task's
					 *	quantum to be applied.
					 */
					was_idle = task_shutdown(task);
					INSIST(!was_idle);
				}

				if (EMPTY(task->events)) {
					/*
					 * Nothing else to do for this task
					 * right now.
					 */
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_EMPTY,
							      "empty"));
					if (task->references == 0 &&
					    TASK_SHUTTINGDOWN(task)) {
						/*
						 * The task is done.
						 */
						XTRACE(isc_msgcat_get(
							       isc_msgcat,
							       ISC_MSGSET_TASK,
							       ISC_MSG_DONE,
							       "done"));
						finished = ISC_TRUE;
						task->state = task_state_done;
					} else
						task->state = task_state_idle;
					done = ISC_TRUE;
				} else if (dispatch_count >= task->quantum) {
					/*
					 * Our quantum has expired, but
					 * there is more work to be done.
					 * We'll requeue it to the ready
					 * queue later.
					 *
					 * We don't check quantum until
					 * dispatching at least one event,
					 * so the minimum quantum is one.
					 */
					XTRACE(isc_msgcat_get(isc_msgcat,
							      ISC_MSGSET_TASK,
							      ISC_MSG_QUANTUM,
							      "quantum"));
					task->state = task_state_ready;
					requeue = ISC_TRUE;
					done = ISC_TRUE;
				}
			} while (!done);
			UNLOCK(&task->lock);

			if (finished)
				task_finished(task);

			LOCK(&manager->lock);
			manager->tasks_running--;
#ifdef ISC_PLATFORM_USETHREADS
			if (manager->exclusive_requested &&
			    manager->tasks_running == 1) {
				SIGNAL(&manager->exclusive_granted);
			}
#endif /* ISC_PLATFORM_USETHREADS */
			if (requeue) {
				/*
				 * We know we're awake, so we don't have
				 * to wakeup any sleeping threads if the
				 * ready queue is empty before we requeue.
				 *
				 * A possible optimization if the queue is
				 * empty is to 'goto' the 'if (task != NULL)'
				 * block, avoiding the ENQUEUE of the task
				 * and the subsequent immediate DEQUEUE
				 * (since it is the only executable task).
				 * We don't do this because then we'd be
				 * skipping the exit_requested check.  The
				 * cost of ENQUEUE is low anyway, especially
				 * when you consider that we'd have to do
				 * an extra EMPTY check to see if we could
				 * do the optimization.  If the ready queue
				 * were usually nonempty, the 'optimization'
				 * might even hurt rather than help.
				 */
#ifdef ISC_PLATFORM_USETHREADS
				ENQUEUE(manager->ready_tasks, task,
					ready_link);
#else
				ENQUEUE(ready_tasks, task, ready_link);
#endif
			}
		}
	}
#ifndef ISC_PLATFORM_USETHREADS
	ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link);
#endif
	UNLOCK(&manager->lock);
}
Beispiel #2
0
isc_result_t
ns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config,
		      cfg_aclconfctx_t *aclconfctx)
{
	controllistener_t *listener;
	controllistenerlist_t new_listeners;
	const cfg_obj_t *controlslist = NULL;
	const cfg_listelt_t *element, *element2;
	char socktext[ISC_SOCKADDR_FORMATSIZE];

	ISC_LIST_INIT(new_listeners);

	/*
	 * Get the list of named.conf 'controls' statements.
	 */
	(void)cfg_map_get(config, "controls", &controlslist);

	/*
	 * Run through the new control channel list, noting sockets that
	 * are already being listened on and moving them to the new list.
	 *
	 * Identifying duplicate addr/port combinations is left to either
	 * the underlying config code, or to the bind attempt getting an
	 * address-in-use error.
	 */
	if (controlslist != NULL) {
		for (element = cfg_list_first(controlslist);
		     element != NULL;
		     element = cfg_list_next(element)) {
			const cfg_obj_t *controls;
			const cfg_obj_t *inetcontrols = NULL;

			controls = cfg_listelt_value(element);
			(void)cfg_map_get(controls, "inet", &inetcontrols);
			if (inetcontrols == NULL)
				continue;

			for (element2 = cfg_list_first(inetcontrols);
			     element2 != NULL;
			     element2 = cfg_list_next(element2)) {
				const cfg_obj_t *control;
				const cfg_obj_t *obj;
				isc_sockaddr_t addr;

				/*
				 * The parser handles BIND 8 configuration file
				 * syntax, so it allows unix phrases as well
				 * inet phrases with no keys{} clause.
				 */
				control = cfg_listelt_value(element2);

				obj = cfg_tuple_get(control, "address");
				addr = *cfg_obj_assockaddr(obj);
				if (isc_sockaddr_getport(&addr) == 0)
					isc_sockaddr_setport(&addr,
							     NS_CONTROL_PORT);

				isc_sockaddr_format(&addr, socktext,
						    sizeof(socktext));

				isc_log_write(ns_g_lctx,
					      NS_LOGCATEGORY_GENERAL,
					      NS_LOGMODULE_CONTROL,
					      ISC_LOG_DEBUG(9),
					      "processing control channel %s",
					      socktext);

				update_listener(cp, &listener, control, config,
						&addr, aclconfctx, socktext,
						isc_sockettype_tcp);

				if (listener != NULL)
					/*
					 * Remove the listener from the old
					 * list, so it won't be shut down.
					 */
					ISC_LIST_UNLINK(cp->listeners,
							listener, link);
				else
					/*
					 * This is a new listener.
					 */
					add_listener(cp, &listener, control,
						     config, &addr, aclconfctx,
						     socktext,
						     isc_sockettype_tcp);

				if (listener != NULL)
					ISC_LIST_APPEND(new_listeners,
							listener, link);
			}
		}
		for (element = cfg_list_first(controlslist);
		     element != NULL;
		     element = cfg_list_next(element)) {
			const cfg_obj_t *controls;
			const cfg_obj_t *unixcontrols = NULL;

			controls = cfg_listelt_value(element);
			(void)cfg_map_get(controls, "unix", &unixcontrols);
			if (unixcontrols == NULL)
				continue;

			for (element2 = cfg_list_first(unixcontrols);
			     element2 != NULL;
			     element2 = cfg_list_next(element2)) {
				const cfg_obj_t *control;
				const cfg_obj_t *path;
				isc_sockaddr_t addr;
				isc_result_t result;

				/*
				 * The parser handles BIND 8 configuration file
				 * syntax, so it allows unix phrases as well
				 * inet phrases with no keys{} clause.
				 */
				control = cfg_listelt_value(element2);

				path = cfg_tuple_get(control, "path");
				result = isc_sockaddr_frompath(&addr,
						      cfg_obj_asstring(path));
				if (result != ISC_R_SUCCESS) {
					isc_log_write(ns_g_lctx,
					      NS_LOGCATEGORY_GENERAL,
					      NS_LOGMODULE_CONTROL,
					      ISC_LOG_DEBUG(9),
					      "control channel '%s': %s",
					      cfg_obj_asstring(path),
					      isc_result_totext(result));
					continue;
				}

				isc_log_write(ns_g_lctx,
					      NS_LOGCATEGORY_GENERAL,
					      NS_LOGMODULE_CONTROL,
					      ISC_LOG_DEBUG(9),
					      "processing control channel '%s'",
					      cfg_obj_asstring(path));

				update_listener(cp, &listener, control, config,
						&addr, aclconfctx,
						cfg_obj_asstring(path),
						isc_sockettype_unix);

				if (listener != NULL)
					/*
					 * Remove the listener from the old
					 * list, so it won't be shut down.
					 */
					ISC_LIST_UNLINK(cp->listeners,
							listener, link);
				else
					/*
					 * This is a new listener.
					 */
					add_listener(cp, &listener, control,
						     config, &addr, aclconfctx,
						     cfg_obj_asstring(path),
						     isc_sockettype_unix);

				if (listener != NULL)
					ISC_LIST_APPEND(new_listeners,
							listener, link);
			}
		}
	} else {
		int i;

		for (i = 0; i < 2; i++) {
			isc_sockaddr_t addr;

			if (i == 0) {
				struct in_addr localhost;

				if (isc_net_probeipv4() != ISC_R_SUCCESS)
					continue;
				localhost.s_addr = htonl(INADDR_LOOPBACK);
				isc_sockaddr_fromin(&addr, &localhost, 0);
			} else {
				if (isc_net_probeipv6() != ISC_R_SUCCESS)
					continue;
				isc_sockaddr_fromin6(&addr,
						     &in6addr_loopback, 0);
			}
			isc_sockaddr_setport(&addr, NS_CONTROL_PORT);

			isc_sockaddr_format(&addr, socktext, sizeof(socktext));

			update_listener(cp, &listener, NULL, NULL,
					&addr, NULL, socktext,
					isc_sockettype_tcp);

			if (listener != NULL)
				/*
				 * Remove the listener from the old
				 * list, so it won't be shut down.
				 */
				ISC_LIST_UNLINK(cp->listeners,
						listener, link);
			else
				/*
				 * This is a new listener.
				 */
				add_listener(cp, &listener, NULL, NULL,
					     &addr, NULL, socktext,
					     isc_sockettype_tcp);

			if (listener != NULL)
				ISC_LIST_APPEND(new_listeners,
						listener, link);
		}
	}

	/*
	 * ns_control_shutdown() will stop whatever is on the global
	 * listeners list, which currently only has whatever sockaddrs
	 * were in the previous configuration (if any) that do not
	 * remain in the current configuration.
	 */
	controls_shutdown(cp);

	/*
	 * Put all of the valid listeners on the listeners list.
	 * Anything already on listeners in the process of shutting
	 * down will be taken care of by listen_done().
	 */
	ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
	return (ISC_R_SUCCESS);
}
Beispiel #3
0
isc_result_t
ns_lwresd_configure(isc_mem_t *mctx, const cfg_obj_t *config) {
	const cfg_obj_t *lwreslist = NULL;
	const cfg_obj_t *lwres = NULL;
	const cfg_obj_t *listenerslist = NULL;
	const cfg_listelt_t *element = NULL;
	ns_lwreslistener_t *listener;
	ns_lwreslistenerlist_t newlisteners;
	isc_result_t result;
	char socktext[ISC_SOCKADDR_FORMATSIZE];
	isc_sockaddr_t *addrs = NULL;
	ns_lwresd_t *lwresd = NULL;
	isc_uint32_t count = 0;

	REQUIRE(mctx != NULL);
	REQUIRE(config != NULL);

	RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);

	ISC_LIST_INIT(newlisteners);

	result = cfg_map_get(config, "lwres", &lwreslist);
	if (result != ISC_R_SUCCESS)
		return (ISC_R_SUCCESS);

	LOCK(&listeners_lock);
	/*
	 * Run through the new lwres address list, noting sockets that
	 * are already being listened on and moving them to the new list.
	 *
	 * Identifying duplicates addr/port combinations is left to either
	 * the underlying config code, or to the bind attempt getting an
	 * address-in-use error.
	 */
	for (element = cfg_list_first(lwreslist);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		in_port_t port;

		lwres = cfg_listelt_value(element);
		CHECK(ns_lwdmanager_create(mctx, lwres, &lwresd));

		port = lwresd_g_listenport;
		if (port == 0)
			port = LWRES_UDP_PORT;

		listenerslist = NULL;
		(void)cfg_map_get(lwres, "listen-on", &listenerslist);
		if (listenerslist == NULL) {
			struct in_addr localhost;
			isc_sockaddr_t address;

			localhost.s_addr = htonl(INADDR_LOOPBACK);
			isc_sockaddr_fromin(&address, &localhost, port);
			CHECK(configure_listener(&address, lwresd, mctx,
						 &newlisteners));
		} else {
			isc_uint32_t i;

			CHECK(ns_config_getiplist(config, listenerslist,
						  port, mctx, &addrs, &count));
			for (i = 0; i < count; i++)
				CHECK(configure_listener(&addrs[i], lwresd,
							 mctx, &newlisteners));
			ns_config_putiplist(mctx, &addrs, count);
		}
		ns_lwdmanager_detach(&lwresd);
	}

	/*
	 * Shutdown everything on the listeners list, and remove them from
	 * the list.  Then put all of the new listeners on it.
	 */

	while (!ISC_LIST_EMPTY(listeners)) {
		listener = ISC_LIST_HEAD(listeners);
		ISC_LIST_UNLINK(listeners, listener, link);

		isc_sockaddr_format(&listener->address,
				    socktext, sizeof(socktext));

		listener_shutdown(listener);
		ns_lwreslistener_detach(&listener);

		isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
			      NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
			      "lwres no longer listening on %s", socktext);
	}

 cleanup:
	ISC_LIST_APPENDLIST(listeners, newlisteners, link);

	if (addrs != NULL)
		ns_config_putiplist(mctx, &addrs, count);

	if (lwresd != NULL)
		ns_lwdmanager_detach(&lwresd);

	UNLOCK(&listeners_lock);

	return (result);
}
Beispiel #4
0
isc_result_t
ns_statschannels_configure(ns_server_t *server, const cfg_obj_t *config,
			 cfg_aclconfctx_t *aclconfctx)
{
	ns_statschannel_t *listener, *listener_next;
	ns_statschannellist_t new_listeners;
	const cfg_obj_t *statschannellist = NULL;
	const cfg_listelt_t *element, *element2;
	char socktext[ISC_SOCKADDR_FORMATSIZE];

	RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);

	ISC_LIST_INIT(new_listeners);

	/*
	 * Get the list of named.conf 'statistics-channels' statements.
	 */
	(void)cfg_map_get(config, "statistics-channels", &statschannellist);

	/*
	 * Run through the new address/port list, noting sockets that are
	 * already being listened on and moving them to the new list.
	 *
	 * Identifying duplicate addr/port combinations is left to either
	 * the underlying config code, or to the bind attempt getting an
	 * address-in-use error.
	 */
	if (statschannellist != NULL) {
#ifndef HAVE_LIBXML2
		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
			      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
			      "statistics-channels specified but not effective "
			      "due to missing XML library");
#endif

		for (element = cfg_list_first(statschannellist);
		     element != NULL;
		     element = cfg_list_next(element)) {
			const cfg_obj_t *statschannel;
			const cfg_obj_t *listenercfg = NULL;

			statschannel = cfg_listelt_value(element);
			(void)cfg_map_get(statschannel, "inet",
					  &listenercfg);
			if (listenercfg == NULL)
				continue;

			for (element2 = cfg_list_first(listenercfg);
			     element2 != NULL;
			     element2 = cfg_list_next(element2)) {
				const cfg_obj_t *listen_params;
				const cfg_obj_t *obj;
				isc_sockaddr_t addr;

				listen_params = cfg_listelt_value(element2);

				obj = cfg_tuple_get(listen_params, "address");
				addr = *cfg_obj_assockaddr(obj);
				if (isc_sockaddr_getport(&addr) == 0)
					isc_sockaddr_setport(&addr, NS_STATSCHANNEL_HTTPPORT);

				isc_sockaddr_format(&addr, socktext,
						    sizeof(socktext));

				isc_log_write(ns_g_lctx,
					      NS_LOGCATEGORY_GENERAL,
					      NS_LOGMODULE_SERVER,
					      ISC_LOG_DEBUG(9),
					      "processing statistics "
					      "channel %s",
					      socktext);

				update_listener(server, &listener,
						listen_params, config, &addr,
						aclconfctx, socktext);

				if (listener != NULL) {
					/*
					 * Remove the listener from the old
					 * list, so it won't be shut down.
					 */
					ISC_LIST_UNLINK(server->statschannels,
							listener, link);
				} else {
					/*
					 * This is a new listener.
					 */
					isc_result_t r;

					r = add_listener(server, &listener,
							 listen_params, config,
							 &addr, aclconfctx,
							 socktext);
					if (r != ISC_R_SUCCESS) {
						cfg_obj_log(listen_params,
							    ns_g_lctx,
							    ISC_LOG_WARNING,
							    "couldn't allocate "
							    "statistics channel"
							    " %s: %s",
							    socktext,
							    isc_result_totext(r));
					}
				}

				if (listener != NULL)
					ISC_LIST_APPEND(new_listeners, listener,
							link);
			}
		}
	}

	for (listener = ISC_LIST_HEAD(server->statschannels);
	     listener != NULL;
	     listener = listener_next) {
		listener_next = ISC_LIST_NEXT(listener, link);
		ISC_LIST_UNLINK(server->statschannels, listener, link);
		shutdown_listener(listener);
	}

	ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link);
	return (ISC_R_SUCCESS);
}