/*
 * Copyright © 2017 Pekka Paalanen <pq@iki.fi>
 * Copyright © 2018 Zodiac Inflight Innovations
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "config.h"

#include <libweston/weston-log.h>
#include "shared/helpers.h"
#include <libweston/libweston.h>

#include "weston-log-internal.h"
#include "weston-debug-server-protocol.h"

#include <assert.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>

/**
 * @defgroup log Public Logging/Debugging API
 * @defgroup internal-log Private/Internal Logging/Debugging API
 * @defgroup debug-protocol weston-debug protocol specific
 */

/** Main weston-log context
 *
 * One per weston_compositor. Stores list of scopes created and a list pending
 * subscriptions.
 *
 * A pending subscription is a subscription to a scope which hasn't been
 * created. When the scope is finally created the pending subscription will be
 * removed from the pending subscription list, but not before was added in the
 * scope's subscription list and that of the subscriber list.
 *
 * Pending subscriptions only make sense for other types of streams, other than
 * those created by weston-debug protocol. In the case of the weston-debug
 * protocol, the subscription processes is done automatically whenever a client
 * connects and subscribes to a scope which was previously advertised by the
 * compositor.
 *
 * @ingroup internal-log
 */
struct weston_log_context {
	struct wl_global *global;
	struct wl_list scope_list; /**< weston_log_scope::compositor_link */
	struct wl_list pending_subscription_list; /**< weston_log_subscription::source_link */
};

/** weston-log message scope
 *
 * This is used for scoping logging/debugging messages. Clients can subscribe
 * to only the scopes they are interested in. A scope is identified by its name
 * (also referred to as debug stream name).
 *
 * @ingroup log
 */
struct weston_log_scope {
	char *name;
	char *desc;
	weston_log_scope_cb new_subscription;
	weston_log_scope_cb destroy_subscription;
	void *user_data;
	struct wl_list compositor_link;
	struct wl_list subscription_list;  /**< weston_log_subscription::source_link */
};

/** Ties a subscriber to a scope
 *
 * A subscription is created each time we'd want to subscribe to a scope. From
 * the stream type we can retrieve the subscriber and from the subscriber we
 * reach each of the streams callbacks. See also weston_log_subscriber object.
 *
 * When a subscription has been created we store it in the scope's subscription
 * list and in the subscriber's subscription list. The subscription might be a
 * pending subscription until the scope for which there's was a subscribe has
 * been created. The scope creation will take of looking through the pending
 * subscription list.
 *
 * A subscription can reached from a subscriber subscription list by using the
 * streams base class.
 *
 * @ingroup internal-log
 */
struct weston_log_subscription {
	struct weston_log_subscriber *owner;
	struct wl_list owner_link;      /**< weston_log_subscriber::subscription_list */

	char *scope_name;
	struct weston_log_scope *source;
	struct wl_list source_link;     /**< weston_log_scope::subscription_list  or
					  weston_log_context::pending_subscription_list */

	void *data;
};

static struct weston_log_subscription *
find_pending_subscription(struct weston_log_context *log_ctx,
			  const char *scope_name)
{
	struct weston_log_subscription *sub;

	wl_list_for_each(sub, &log_ctx->pending_subscription_list, source_link)
		if (!strcmp(sub->scope_name, scope_name))
			return sub;

	return NULL;
}

/** Create a pending subscription and add it the list of pending subscriptions
 *
 * @param owner a subscriber represented by weston_log_subscriber object
 * @param scope_name the name of the scope (which we don't have in the list of scopes)
 * @param log_ctx the logging context used to add the pending subscription
 *
 * @memberof weston_log_subscription
 */
static void
weston_log_subscription_create_pending(struct weston_log_subscriber *owner,
				       const char *scope_name,
				       struct weston_log_context *log_ctx)
{
	assert(owner);
	assert(scope_name);
	struct weston_log_subscription *sub = zalloc(sizeof(*sub));

	if (!sub)
		return;

	sub->scope_name = strdup(scope_name);
	sub->owner = owner;

	wl_list_insert(&log_ctx->pending_subscription_list, &sub->source_link);
}

/** Destroys the pending subscription created previously with
 * weston_log_subscription_create_pending()
 *
 * @param sub the weston_log_subscription object to remove from the list
 * of subscriptions and to destroy the subscription
 *
 * @memberof weston_log_subscription
 */
static void
weston_log_subscription_destroy_pending(struct weston_log_subscription *sub)
{
	assert(sub);
	/* pending subsriptions do not have a source */
	wl_list_remove(&sub->source_link);
	free(sub->scope_name);
	free(sub);
}

/** Write to the stream's subscription
 *
 * @memberof weston_log_subscription
 */
static void
weston_log_subscription_write(struct weston_log_subscription *sub,
			      const char *data, size_t len)
{
	if (sub->owner && sub->owner->write)
		sub->owner->write(sub->owner, data, len);
}

/** Write a formatted string to the stream's subscription
 *
 * @memberof weston_log_subscription
 */
static void
weston_log_subscription_vprintf(struct weston_log_subscription *sub,
				const char *fmt, va_list ap)
{
	static const char oom[] = "Out of memory";
	char *str;
	int len;

	if (!weston_log_scope_is_enabled(sub->source))
		return;

	len = vasprintf(&str, fmt, ap);
	if (len >= 0) {
		weston_log_subscription_write(sub, str, len);
		free(str);
	} else {
		weston_log_subscription_write(sub, oom, sizeof oom - 1);
	}
}

void
weston_log_subscription_set_data(struct weston_log_subscription *sub, void *data)
{
	/* don't allow data to already be set */
	assert(!sub->data);
	sub->data = data;
}

void *
weston_log_subscription_get_data(struct weston_log_subscription *sub)
{
	return sub->data;
}

/** Creates a new subscription using the subscriber by \c owner.
 *
 * The subscription created is added to the \c owner subscription list.
 * Destroying the subscription using weston_log_subscription_destroy() will
 * remove the link from the subscription list and free storage alloc'ed.
 *
 * Note that this adds the subscription to the scope's subscription list
 * hence the \c scope required argument.
 *
 * @param owner the subscriber owner, must be created before creating a
 * subscription
 * @param scope the scope in order to add the subscription to the scope's
 * subscription list
 * @returns a weston_log_subscription object in case of success, or NULL
 * otherwise
 *
 * @sa weston_log_subscription_destroy, weston_log_subscription_remove,
 * weston_log_subscription_add
 * @memberof weston_log_subscription
 */
void
weston_log_subscription_create(struct weston_log_subscriber *owner,
			       struct weston_log_scope *scope)
{
	struct weston_log_subscription *sub;
	assert(owner);
	assert(scope);
	assert(scope->name);

	sub = zalloc(sizeof(*sub));
	if (!sub)
		return;

	sub->owner = owner;
	sub->scope_name = strdup(scope->name);

	wl_list_insert(&sub->owner->subscription_list, &sub->owner_link);

	weston_log_subscription_add(scope, sub);
	weston_log_run_cb_new_subscription(sub);
}

/** Destroys the subscription
 *
 * Removes the subscription from the scopes subscription list and from
 * subscriber's subscription list. It destroys the subscription afterwads.
 *
 * @memberof weston_log_subscription
 */
void
weston_log_subscription_destroy(struct weston_log_subscription *sub)
{
	assert(sub);

	if (sub->source->destroy_subscription)
		sub->source->destroy_subscription(sub, sub->source->user_data);

	if (sub->owner)
		wl_list_remove(&sub->owner_link);

	weston_log_subscription_remove(sub);
	free(sub->scope_name);
	free(sub);
}

/** Retrieve a subscription by using the subscriber
 *
 * This is useful when trying to find a subscription from the subscriber by
 * having only access to the stream.
 *
 * @param subscriber the subscriber in question
 * @returns a weston_log_subscription object
 *
 * @memberof weston_log_subscription
 */
struct weston_log_subscription *
weston_log_subscriber_get_only_subscription(struct weston_log_subscriber *subscriber)
{
	struct weston_log_subscription *sub;
	/* unlikely, but can happen */
	if (wl_list_length(&subscriber->subscription_list) == 0)
		return NULL;

	assert(wl_list_length(&subscriber->subscription_list) == 1);

	return wl_container_of(subscriber->subscription_list.prev,
			       sub, owner_link);
}

/** Adds the subscription \c sub to the subscription list of the
 * scope.
 *
 * This should used when the scope has been created, and the subscription \c
 * sub has be created before calling this function.
 *
 * @param scope the scope
 * @param sub the subscription, it must be created before, see
 * weston_log_subscription_create()
 *
 * @memberof weston_log_subscription
 */
void
weston_log_subscription_add(struct weston_log_scope *scope,
			    struct weston_log_subscription *sub)
{
	assert(scope);
	assert(sub);
	/* don't allow subscriptions to have a source already! */
	assert(!sub->source);

	sub->source = scope;
	wl_list_insert(&scope->subscription_list, &sub->source_link);
}

/** Removes the subscription from the scope's subscription list
 *
 * @memberof weston_log_subscription
 */
void
weston_log_subscription_remove(struct weston_log_subscription *sub)
{
	assert(sub);
	if (sub->source)
		wl_list_remove(&sub->source_link);
	sub->source = NULL;
}

/** Look-up the scope from the scope list  stored in the log context, by
 * matching against the \c name.
 *
 * @param log_ctx
 * @param name the scope name, see weston_compositor_add_log_scope()
 * @returns NULL if none found, or a pointer to a weston_log_scope
 *
 * @ingroup internal-log
 */
struct weston_log_scope *
weston_log_get_scope(struct weston_log_context *log_ctx, const char *name)
{
	struct weston_log_scope *scope;
	wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
		if (strcmp(name, scope->name) == 0)
			return scope;
	return NULL;
}

/** Wrapper to invoke the weston_log_scope_cb. Allows to call the cb
 * new_subscription of a log scope.
 *
 * @ingroup internal-log
 */
void
weston_log_run_cb_new_subscription(struct weston_log_subscription *sub)
{
	if (sub->source->new_subscription)
		sub->source->new_subscription(sub, sub->source->user_data);
}

/** Advertise the log scope name and the log scope description
 *
 * This is only used by the weston-debug protocol!
 *
 * @ingroup internal-log
 */
void
weston_debug_protocol_advertise_scopes(struct weston_log_context *log_ctx,
				       struct wl_resource *res)
{
	struct weston_log_scope *scope;
	wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
		weston_debug_v1_send_available(res, scope->name, scope->desc);
}

/**
 * Connect weston_compositor structure to weston_log_context structure.
 *
 * \param compositor
 * \param log_ctx
 * \return 0 on success, -1 on failure
 *
 * Sets weston_compositor::weston_log_ctx.
 *
 * @ingroup log
 */
int
weston_log_ctx_compositor_setup(struct weston_compositor *compositor,
			      struct weston_log_context *log_ctx)
{
	assert(!compositor->weston_log_ctx);
	assert(log_ctx);

	compositor->weston_log_ctx = log_ctx;
	return 0;
}

/** Creates  weston_log_context structure
 *
 * \return NULL in case of failure, or a weston_log_context object in case of
 * success
 *
 * weston_log_context is a singleton for each weston_compositor.
 * @ingroup log
 *
 */
WL_EXPORT struct weston_log_context *
weston_log_ctx_compositor_create(void)
{
	struct weston_log_context *log_ctx;

	log_ctx = zalloc(sizeof *log_ctx);
	if (!log_ctx)
		return NULL;

	wl_list_init(&log_ctx->scope_list);
	wl_list_init(&log_ctx->pending_subscription_list);

	return log_ctx;
}

/** Destroy weston_log_context structure
 *
 * \param compositor The libweston compositor whose weston-debug to tear down.
 *
 * Clears weston_compositor::weston_log_ctx.
 * @ingroup log
 *
 */
WL_EXPORT void
weston_log_ctx_compositor_destroy(struct weston_compositor *compositor)
{
	struct weston_log_context *log_ctx = compositor->weston_log_ctx;
	struct weston_log_scope *scope;
	struct weston_log_subscription *pending_sub, *pending_sub_tmp;

	if (log_ctx->global)
		wl_global_destroy(log_ctx->global);

	wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
		fprintf(stderr, "Internal warning: debug scope '%s' has not been destroyed.\n",
			   scope->name);

	/* Remove head to not crash if scope removed later. */
	wl_list_remove(&log_ctx->scope_list);

	/* Remove any pending subscription(s) which nobody subscribed to */
	wl_list_for_each_safe(pending_sub, pending_sub_tmp,
			      &log_ctx->pending_subscription_list, source_link) {
		weston_log_subscription_destroy_pending(pending_sub);
	}

	/* pending_subscription_list should be empty at this point */

	free(log_ctx);

	compositor->weston_log_ctx = NULL;
}

/** Enable weston-debug protocol extension
 *
 * \param compositor The libweston compositor where to enable.
 *
 * This enables the weston_debug_v1 Wayland protocol extension which any client
 * can use to get debug messages from the compositor.
 *
 * WARNING: This feature should not be used in production. If a client
 * provides a file descriptor that blocks writes, it will block the whole
 * compositor indefinitely.
 *
 * There is no control on which client is allowed to subscribe to debug
 * messages. Any and all clients are allowed.
 *
 * The debug extension is disabled by default, and once enabled, cannot be
 * disabled again.
 *
 * @ingroup debug-protocol
 */
WL_EXPORT void
weston_compositor_enable_debug_protocol(struct weston_compositor *compositor)
{
	struct weston_log_context *log_ctx = compositor->weston_log_ctx;
	assert(log_ctx);
	if (log_ctx->global)
		return;

	log_ctx->global = wl_global_create(compositor->wl_display,
				       &weston_debug_v1_interface, 1,
				       log_ctx, weston_log_bind_weston_debug);
	if (!log_ctx->global)
		return;

	fprintf(stderr, "WARNING: debug protocol has been enabled. "
		   "This is a potential denial-of-service attack vector and "
		   "information leak.\n");
}

/** Determine if the debug protocol has been enabled
 *
 * \param wc The libweston compositor to verify if debug protocol has been
 * enabled
 *
 * @ingroup debug-protocol
 */
WL_EXPORT bool
weston_compositor_is_debug_protocol_enabled(struct weston_compositor *wc)
{
	return wc->weston_log_ctx->global != NULL;
}

/** Register a new stream name, creating a log scope.
 *
 * @param log_ctx The weston_log_context where to add.
 * @param name The debug stream/scope name; must not be NULL.
 * @param description The log scope description for humans; must not be NULL.
 * @param new_subscription Optional callback when a client subscribes to this
 * scope.
 * @param destroy_subscription Optional callback when a client destroys the
 * subscription.
 * @param user_data Optional user data pointer for the callback.
 * @returns A valid pointer on success, NULL on failure.
 *
 * This function is used to create a log scope. All debug message printing
 * happens for a scope, which allows clients to subscribe to the kind of debug
 * messages they want by \c name. For the weston-debug protocol,
 * subscription for the scope will happen automatically but for other types of
 * streams, weston_log_subscribe() should be called as to create a subscription
 * and tie it to the scope created by this function.
 *
 * \p name must be unique in the weston_compositor instance. \p name
 * and \p description must both be provided. In case of the weston-debug
 * protocol, the description is printed when a client asks for a list of
 * supported log scopes.
 *
 * \p new_subscription, if not NULL, is called when a client subscribes to the log
 * scope creating a debug stream. This is for log scopes that need to print
 * messages as a response to a client appearing, e.g. printing a list of
 * windows on demand or a static preamble. The argument \p user_data is
 * passed in to the callback and is otherwise unused.
 *
 * For one-shot debug streams, \c new_subscription should finally call
 * weston_log_subscription_complete() to close the stream and tell the client
 * the printing is complete. Otherwise the client expects more data to be
 * written.  The complete callback in weston_log_subscriber should be installed
 * to trigger it and it is set-up automatically for the weston-debug protocol.
 *
 * As subscription can take place before creating the scope, any pending
 * subscriptions to scope added by weston_log_subscribe(), will be checked
 * against the scope being created and if found will be added to the scope's
 * subscription list.
 *
 * The log scope must be destroyed using weston_compositor_log_scope_destroy()
 * before destroying the weston_compositor.
 *
 * @memberof weston_log_scope
 * @sa weston_log_scope_cb, weston_log_subscribe
 */
WL_EXPORT struct weston_log_scope *
weston_compositor_add_log_scope(struct weston_log_context *log_ctx,
				const char *name,
				const char *description,
				weston_log_scope_cb new_subscription,
				weston_log_scope_cb destroy_subscription,
				void *user_data)
{
	struct weston_log_scope *scope;
	struct weston_log_subscription *pending_sub = NULL;

	if (!name || !description) {
		fprintf(stderr, "Error: cannot add a debug scope without name or description.\n");
		return NULL;
	}

	if (!log_ctx) {
		fprintf(stderr, "Error: cannot add debug scope '%s', infra not initialized.\n",
			   name);
		return NULL;
	}

	if (weston_log_get_scope(log_ctx, name)){
		fprintf(stderr, "Error: debug scope named '%s' is already registered.\n",
			   name);
		return NULL;
	}

	scope = zalloc(sizeof *scope);
	if (!scope) {
		fprintf(stderr, "Error adding debug scope '%s': out of memory.\n",
			   name);
		return NULL;
	}

	scope->name = strdup(name);
	scope->desc = strdup(description);
	scope->new_subscription = new_subscription;
	scope->destroy_subscription = destroy_subscription;
	scope->user_data = user_data;
	wl_list_init(&scope->subscription_list);

	if (!scope->name || !scope->desc) {
		fprintf(stderr, "Error adding debug scope '%s': out of memory.\n",
			   name);
		free(scope->name);
		free(scope->desc);
		free(scope);
		return NULL;
	}

	wl_list_insert(log_ctx->scope_list.prev, &scope->compositor_link);

	/* check if there are any pending subscriptions to this scope */
	while ((pending_sub = find_pending_subscription(log_ctx, scope->name)) != NULL) {
		weston_log_subscription_create(pending_sub->owner, scope);

		/* remove it from pending */
		weston_log_subscription_destroy_pending(pending_sub);
	}


	return scope;
}

/** Destroy a log scope
 *
 * @param scope The log scope to destroy; may be NULL.
 *
 * Destroys the log scope, calling each stream's destroy callback if one was
 * installed/created.
 *
 * @memberof weston_log_scope
 */
WL_EXPORT void
weston_compositor_log_scope_destroy(struct weston_log_scope *scope)
{
	struct weston_log_subscription *sub, *sub_tmp;

	if (!scope)
		return;

	wl_list_for_each_safe(sub, sub_tmp, &scope->subscription_list, source_link) {
		/* destroy each subscription */
		if (sub->owner->destroy)
			sub->owner->destroy(sub->owner);

		weston_log_subscription_destroy(sub);
	}

	wl_list_remove(&scope->compositor_link);
	free(scope->name);
	free(scope->desc);
	free(scope);
}

/** Are there any active subscriptions to the scope?
 *
 * \param scope The log scope to check; may be NULL.
 * \return True if any streams are open for this scope, false otherwise.
 *
 * As printing some debugging messages may be relatively expensive, one
 * can use this function to determine if there is a need to gather the
 * debugging information at all. If this function returns false, all
 * printing for this scope is dropped, so gathering the information is
 * pointless.
 *
 * The return value of this function should not be stored, as new clients
 * may subscribe to the debug scope later.
 *
 * If the given scope is NULL, this function will always return false,
 * making it safe to use in teardown or destroy code, provided the
 * scope is initialized to NULL before creation and set to NULL after
 * destruction.
 *
 * \memberof weston_log_scope
 */
WL_EXPORT bool
weston_log_scope_is_enabled(struct weston_log_scope *scope)
{
	if (!scope)
		return false;

	return !wl_list_empty(&scope->subscription_list);
}

/** Close the stream's complete callback if one was installed/created.
 *
 * @ingroup log
 */
WL_EXPORT void
weston_log_subscription_complete(struct weston_log_subscription *sub)
{
	if (sub->owner && sub->owner->complete)
		sub->owner->complete(sub->owner);
}

/** Close the log scope.
 *
 * @param scope The log scope to complete; may be NULL.
 *
 * Complete the log scope, calling each stream's complete callback if one was
 * installed/created. This can be useful to signal the reading end that the
 * data has been transmited and should no longer expect that written over the
 * stream. Particularly useful for the weston-debug protocol.
 *
 * @memberof weston_log_scope
 * @sa weston_compositor_add_log_scope, weston_compositor_log_scope_destroy
 */
WL_EXPORT void
weston_log_scope_complete(struct weston_log_scope *scope)
{
	struct weston_log_subscription *sub;

	if (!scope)
		return;

	wl_list_for_each(sub, &scope->subscription_list, source_link)
		weston_log_subscription_complete(sub);
}

/** Write log data for a scope
 *
 * \param scope The debug scope to write for; may be NULL, in which case
 *              nothing will be written.
 * \param[in] data Pointer to the data to write.
 * \param len Number of bytes to write.
 *
 * Writes the given data to all subscribed clients' streams.
 *
 * \memberof weston_log_scope
 */
WL_EXPORT void
weston_log_scope_write(struct weston_log_scope *scope,
		       const char *data, size_t len)
{
	struct weston_log_subscription *sub;

	if (!scope)
		return;

	wl_list_for_each(sub, &scope->subscription_list, source_link)
		weston_log_subscription_write(sub, data, len);
}

/** Write a formatted string for a scope (varargs)
 *
 * \param scope The log scope to write for; may be NULL, in which case
 *              nothing will be written.
 * \param fmt Printf-style format string.
 * \param ap Formatting arguments.
 *
 * Writes to formatted string to all subscribed clients' streams.
 *
 * The behavioral details for each stream are the same as for
 * weston_debug_stream_write().
 *
 * \memberof weston_log_scope
 */
WL_EXPORT int
weston_log_scope_vprintf(struct weston_log_scope *scope,
			 const char *fmt, va_list ap)
{
	static const char oom[] = "Out of memory";
	char *str;
	int len = 0;

	if (!weston_log_scope_is_enabled(scope))
		return len;

	len = vasprintf(&str, fmt, ap);
	if (len >= 0) {
		weston_log_scope_write(scope, str, len);
		free(str);
	} else {
		weston_log_scope_write(scope, oom, sizeof oom - 1);
	}

	return len;
}

/** Write a formatted string for a scope
 *
 * \param scope The log scope to write for; may be NULL, in which case
 *              nothing will be written.
 * \param fmt Printf-style format string and arguments.
 *
 * Writes to formatted string to all subscribed clients' streams.
 *
 * The behavioral details for each stream are the same as for
 * weston_debug_stream_write().
 *
 * \memberof weston_log_scope
 */
WL_EXPORT int
weston_log_scope_printf(struct weston_log_scope *scope,
			  const char *fmt, ...)
{
	va_list ap;
	int len;

	va_start(ap, fmt);
	len = weston_log_scope_vprintf(scope, fmt, ap);
	va_end(ap);

	return len;
}

/** Write a formatted string for a subscription
 *
 * \param sub The subscription to write for; may be NULL, in which case
 *              nothing will be written.
 * \param fmt Printf-style format string and arguments.
 *
 * Writes to formatted string to the stream that created the subscription.
 *
 * @ingroup log
 */
WL_EXPORT void
weston_log_subscription_printf(struct weston_log_subscription *sub,
			       const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	weston_log_subscription_vprintf(sub, fmt, ap);
	va_end(ap);
}

/** Write debug scope name and current time into string
 *
 * \param[in] scope debug scope; may be NULL
 * \param[out] buf Buffer to store the string.
 * \param len Available size in the buffer in bytes.
 * \return \c buf
 *
 * Reads the current local wall-clock time and formats it into a string.
 * and append the debug scope name to it, if a scope is available.
 * The string is NUL-terminated, even if truncated.
 *
 * @memberof weston_log_scope
 */
WL_EXPORT char *
weston_log_scope_timestamp(struct weston_log_scope *scope,
			   char *buf, size_t len)
{
	struct timeval tv;
	struct tm *bdt;
	char string[128];
	size_t ret = 0;

	gettimeofday(&tv, NULL);

	bdt = localtime(&tv.tv_sec);
	if (bdt)
		ret = strftime(string, sizeof string,
			       "%Y-%m-%d %H:%M:%S", bdt);

	if (ret > 0) {
		snprintf(buf, len, "[%s.%03ld][%s]", string,
			 tv.tv_usec / 1000,
			 (scope) ? scope->name : "no scope");
	} else {
		snprintf(buf, len, "[?][%s]",
			 (scope) ? scope->name : "no scope");
	}

	return buf;
}

/** Subscribe to a scope
 *
 * Creates a subscription which is used to subscribe the \p subscriber
 * to the scope \c scope_name.
 *
 * If \c scope_name has already been created (using
 * weston_compositor_add_log_scope) the subscription will take place
 * immediately, otherwise we store the subscription into a pending list. See
 * also weston_compositor_add_log_scope().
 *
 * @param log_ctx the log context, used for accessing pending list
 * @param subscriber the subscriber, which has to be created before
 * @param scope_name the scope name. In case the scope is not created
 * we temporarily store the subscription in the pending list.
 *
 * @ingroup log
 */
WL_EXPORT void
weston_log_subscribe(struct weston_log_context *log_ctx,
		     struct weston_log_subscriber *subscriber,
		     const char *scope_name)
{
	assert(log_ctx);
	assert(subscriber);
	assert(scope_name);

	struct weston_log_scope *scope;

	scope = weston_log_get_scope(log_ctx, scope_name);
	if (scope)
		weston_log_subscription_create(subscriber, scope);
	else
		/*
		 * if we don't have already as scope for it, add it to pending
		 * subscription list
		 */
		weston_log_subscription_create_pending(subscriber, scope_name, log_ctx);
}

/** Iterate over all subscriptions in a scope
 *
 * @param scope the scope for which you want to iterate
 * @param sub_iter the iterator, use NULL to start from the 'head'
 * @returns the next subscription from the log scope
 *
 * This is (quite) useful when 'log_scope' and 'log_subscription' are opaque. Do note
 * that \c sub_iter needs to be NULL-initialized before calling this function.
 *
 */
WL_EXPORT struct weston_log_subscription *
weston_log_subscription_iterate(struct weston_log_scope *scope,
				struct weston_log_subscription *sub_iter)
{
	struct wl_list *list = &scope->subscription_list;
	struct wl_list *node;

	/* go to the next item in the list or if not set starts with the head */
	if (sub_iter)
		node = sub_iter->source_link.next;
	else
		node = list->next;

	assert(node);
	assert(!sub_iter || node != &sub_iter->source_link);

	/* if we're at the end */
	if (node == list)
		return NULL;

	return container_of(node, struct weston_log_subscription, source_link);
}
